From f4baf995ae281927cc157dc32015d0250192efbf Mon Sep 17 00:00:00 2001 From: Jonathan Amend Date: Wed, 15 Jan 2020 18:45:40 -0500 Subject: [PATCH 1/5] Cast null string parameters to varchar Fixes npgsql/EntityFramework6.Npgsql#121 --- EF6.PG.Tests/EntityFrameworkBasicTests.cs | 8 ++++++-- EF6.PG/SqlGenerators/SqlBaseGenerator.cs | 10 +++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/EF6.PG.Tests/EntityFrameworkBasicTests.cs b/EF6.PG.Tests/EntityFrameworkBasicTests.cs index 4e89635..8aed7e5 100644 --- a/EF6.PG.Tests/EntityFrameworkBasicTests.cs +++ b/EF6.PG.Tests/EntityFrameworkBasicTests.cs @@ -54,7 +54,11 @@ public void InsertAndSelect() } var someParameter = "Some"; Assert.IsTrue(context.Posts.Any(p => p.Title.StartsWith(someParameter))); + Assert.IsTrue(context.Posts.All(p => p.Title != null)); + Assert.IsTrue(context.Posts.Any(p => someParameter != null)); Assert.IsTrue(context.Posts.Select(p => p.VarbitColumn == varbitVal).First()); + Assert.IsTrue(context.Posts.All(p => p.VarbitColumn != null)); + Assert.IsTrue(context.Posts.Any(p => varbitVal != null)); Assert.IsTrue(context.Posts.Select(p => p.VarbitColumn == "10011").First()); Assert.AreEqual(1, context.NoColumnsEntities.Count()); } @@ -791,7 +795,7 @@ public void Test_string_null_propagation() Console.WriteLine(query.ToString()); StringAssert.AreEqualIgnoringCase( "SELECT CASE WHEN (COALESCE(@p__linq__0,E'default_value') IS NULL) THEN (E'')" - + " WHEN (@p__linq__0 IS NULL) THEN (E'default_value') ELSE (@p__linq__0) END ||" + + " WHEN (CAST (@p__linq__0 AS varchar) IS NULL) THEN (E'default_value') ELSE (@p__linq__0) END ||" + " E'_postfix' AS \"C1\" FROM \"dbo\".\"Blogs\" AS \"Extent1\"", query.ToString()); } @@ -819,7 +823,7 @@ public void Test_string_multiple_null_propagation() Console.WriteLine(query.ToString()); StringAssert.AreEqualIgnoringCase( "SELECT CASE WHEN (COALESCE(@p__linq__0,COALESCE(@p__linq__1,@p__linq__2)) IS NULL)" - + " THEN (E'') WHEN (@p__linq__0 IS NULL) THEN (COALESCE(@p__linq__1,@p__linq__2)) ELSE" + + " THEN (E'') WHEN (CAST (@p__linq__0 AS varchar) IS NULL) THEN (COALESCE(@p__linq__1,@p__linq__2)) ELSE" + " (@p__linq__0) END || E'_postfix' AS \"C1\" FROM \"dbo\".\"Blogs\" AS \"Extent1\"", query.ToString()); } diff --git a/EF6.PG/SqlGenerators/SqlBaseGenerator.cs b/EF6.PG/SqlGenerators/SqlBaseGenerator.cs index b36413a..6caf918 100644 --- a/EF6.PG/SqlGenerators/SqlBaseGenerator.cs +++ b/EF6.PG/SqlGenerators/SqlBaseGenerator.cs @@ -642,7 +642,15 @@ public override VisitedExpression Visit([NotNull] DbIsOfExpression expression) } public override VisitedExpression Visit([NotNull] DbIsNullExpression expression) - => OperatorExpression.Build(Operator.IsNull, _useNewPrecedences, expression.Argument.Accept(this)); + { + if (expression.Argument.ExpressionKind == DbExpressionKind.ParameterReference && + (expression.Argument.ResultType.EdmType as PrimitiveType)?.PrimitiveTypeKind == PrimitiveTypeKind.String) + { + var castedParameterExpression = new CastExpression(expression.Argument.Accept(this), "varchar"); + return OperatorExpression.Build(Operator.IsNull, _useNewPrecedences, castedParameterExpression); + } + return OperatorExpression.Build(Operator.IsNull, _useNewPrecedences, expression.Argument.Accept(this)); + } // NOT EXISTS public override VisitedExpression Visit([NotNull] DbIsEmptyExpression expression) From cbaaad9dc5b362a0053af3dd88858563e0a661cb Mon Sep 17 00:00:00 2001 From: Jonathan Amend Date: Thu, 3 Dec 2020 22:35:40 -0500 Subject: [PATCH 2/5] Add NpgsqlDateTimeFunctions.Timezone --- EF6.PG.Tests/EntityFrameworkBasicTests.cs | 22 +++++++++++++++++++ .../Support/EntityFrameworkTestBase.cs | 9 ++++++++ EF6.PG/NpgsqlDateTimeFunctions.cs | 20 +++++++++++++++++ EF6.PG/NpgsqlProviderManifest.cs | 2 +- 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 EF6.PG/NpgsqlDateTimeFunctions.cs diff --git a/EF6.PG.Tests/EntityFrameworkBasicTests.cs b/EF6.PG.Tests/EntityFrameworkBasicTests.cs index 8aed7e5..669319c 100644 --- a/EF6.PG.Tests/EntityFrameworkBasicTests.cs +++ b/EF6.PG.Tests/EntityFrameworkBasicTests.cs @@ -151,6 +151,28 @@ public void SelectWithWhere_Ef_TruncateTime() } } + [Test] + public void Select_Ef_Timezone() + { + var createdOnDate = new DateTimeOffset(2020, 12, 03, 22, 23, 0, TimeSpan.Zero); + using (var context = new BloggingContext(ConnectionString)) + { + context.Logs.Add(new Log() + { + CreationDate = createdOnDate + }); + context.SaveChanges(); + } + + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.ExecuteSqlCommand("SET TIMEZONE='UTC';"); + var query = context.Logs.Select(p => NpgsqlDateTimeFunctions.Timezone("Pacific/Honolulu", p.CreationDate)); + var createdOnDateInTimeZone = query.FirstOrDefault(); + Assert.AreEqual(new DateTime(2020, 12, 03, 12, 23, 0), createdOnDateInTimeZone); + } + } + [Test] public void SelectWithLike_SpecialCharacters() { diff --git a/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs b/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs index d07af64..b484e63 100644 --- a/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs +++ b/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs @@ -51,6 +51,7 @@ protected void SetUp() { context.Blogs.RemoveRange(context.Blogs); context.Posts.RemoveRange(context.Posts); + context.Logs.RemoveRange(context.Logs); context.NoColumnsEntities.RemoveRange(context.NoColumnsEntities); context.SaveChanges(); } @@ -80,6 +81,12 @@ public class Post public virtual Blog Blog { get; set; } } + public class Log + { + public int Id { get; set; } + public DateTimeOffset CreationDate { get; set; } + } + public class ClrEnumEntity { public int Id { get; set; } @@ -156,6 +163,7 @@ public BloggingContext(string connection) public DbSet Blogs { get; set; } public DbSet Posts { get; set; } + public DbSet Logs { get; set; } public DbSet NoColumnsEntities { get; set; } public DbSet ClrEnumEntities { get; set; } public DbSet ClrEnumCompositeKeyEntities { get; set; } @@ -191,6 +199,7 @@ private static DbCompiledModel CreateModel(NpgsqlConnection connection) // Import Sets dbModelBuilder.Entity(); dbModelBuilder.Entity(); + dbModelBuilder.Entity(); dbModelBuilder.Entity(); dbModelBuilder.Entity(); dbModelBuilder.Entity(); diff --git a/EF6.PG/NpgsqlDateTimeFunctions.cs b/EF6.PG/NpgsqlDateTimeFunctions.cs new file mode 100644 index 0000000..5d035ec --- /dev/null +++ b/EF6.PG/NpgsqlDateTimeFunctions.cs @@ -0,0 +1,20 @@ +using System; +using System.Data.Entity; +using System.Diagnostics.CodeAnalysis; + +namespace Npgsql +{ + /// + /// Use this class in LINQ queries to emit timestamp manipulation SQL fragments. + /// + [SuppressMessage("ReSharper", "UnusedParameter.Global")] + public static class NpgsqlDateTimeFunctions + { + /// + /// Convert a timestamptz to a timezone + /// + [DbFunction("Npgsql", "timezone")] + public static DateTime Timezone(string zone, DateTimeOffset timestamp) + => throw new NotSupportedException(); + } +} diff --git a/EF6.PG/NpgsqlProviderManifest.cs b/EF6.PG/NpgsqlProviderManifest.cs index a76542a..3d7e4c3 100644 --- a/EF6.PG/NpgsqlProviderManifest.cs +++ b/EF6.PG/NpgsqlProviderManifest.cs @@ -320,7 +320,7 @@ public override string EscapeLikeArgument([NotNull] string argument) public override bool SupportsInExpression() => true; public override ReadOnlyCollection GetStoreFunctions() - => new[] { typeof(NpgsqlTextFunctions).GetTypeInfo(), typeof(NpgsqlTypeFunctions) } + => new[] { typeof(NpgsqlTextFunctions).GetTypeInfo(), typeof(NpgsqlTypeFunctions), typeof(NpgsqlDateTimeFunctions) } .SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.Static)) .Select(x => new { Method = x, DbFunction = x.GetCustomAttribute() }) .Where(x => x.DbFunction != null) From fd2297366b2112f6a0763004af1cc67ed1847e0d Mon Sep 17 00:00:00 2001 From: Jonathan Amend Date: Thu, 3 Dec 2020 22:42:21 -0500 Subject: [PATCH 3/5] Version bump --- EF6.PG/EF6.PG.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EF6.PG/EF6.PG.csproj b/EF6.PG/EF6.PG.csproj index 86e2e0b..32fb4d7 100644 --- a/EF6.PG/EF6.PG.csproj +++ b/EF6.PG/EF6.PG.csproj @@ -5,7 +5,7 @@ Copyright 2019 © The Npgsql Development Team Npgsql npgsql postgresql postgres data database entity framework ef orm - 6.4.0 + 6.4.1 latest net45;net461;netstandard21 true From e751cc9a8ebd98b18611601c29a7125ae6ce4dd3 Mon Sep 17 00:00:00 2001 From: Jonathan Amend Date: Thu, 25 Mar 2021 17:57:26 -0400 Subject: [PATCH 4/5] Support StringAgg -> string_agg(x, ', ') --- EF6.PG.Tests/EntityFrameworkBasicTests.cs | 49 +++++++++++++++++++ .../Support/EntityFrameworkTestBase.cs | 9 +++- EF6.PG/EF6.PG.csproj | 4 +- EF6.PG/NpgsqlAggregateFunctions.cs | 21 ++++++++ EF6.PG/NpgsqlProviderManifest.cs | 24 ++++++++- .../PublishProfiles/FolderProfile.pubxml | 13 +++++ EF6.PG/SqlGenerators/SqlBaseGenerator.cs | 34 +++++++++++++ EntityFramework6.Npgsql.sln | 7 +-- 8 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 EF6.PG/NpgsqlAggregateFunctions.cs create mode 100644 EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml diff --git a/EF6.PG.Tests/EntityFrameworkBasicTests.cs b/EF6.PG.Tests/EntityFrameworkBasicTests.cs index 669319c..0f671ad 100644 --- a/EF6.PG.Tests/EntityFrameworkBasicTests.cs +++ b/EF6.PG.Tests/EntityFrameworkBasicTests.cs @@ -173,6 +173,55 @@ public void Select_Ef_Timezone() } } + [Test] + public void Select_Ef_StringAgg() + { + DateTime createdOnDate = new DateTime(2014, 05, 08); + using (var context = new BloggingContext(ConnectionString)) + { + var blog = new Blog() + { + Name = "Blog 1" + }; + blog.Posts = new List(); + + blog.Posts.Add(new Post() + { + Content = "Content 1", + Rating = 1, + Title = "Title 1", + CreationDate = createdOnDate + }); + blog.Posts.Add(new Post() + { + Content = "Content 2", + Rating = 2, + Title = "Title 2", + CreationDate = createdOnDate + }); + blog.Posts.Add(new Post() + { + Content = "Content 3", + Rating = 3, + Title = "Title 3", + CreationDate = createdOnDate + }); + + context.Blogs.Add(blog); + context.SaveChanges(); + } + + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Initialize(true); + var query = context.Posts + .GroupBy(p => p.BlogId) + .Select(g => g.Select(x => x.Title).StringAgg()); + var result = query.FirstOrDefault(); + Assert.AreEqual("Title 1, Title 2, Title 3", result); + } + } + [Test] public void SelectWithLike_SpecialCharacters() { diff --git a/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs b/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs index b484e63..646e017 100644 --- a/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs +++ b/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs @@ -12,6 +12,8 @@ using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; +using System.Data.Entity.ModelConfiguration.Conventions; +using System.Reflection; using NpgsqlTypes; // ReSharper disable once CheckNamespace @@ -170,7 +172,7 @@ public BloggingContext(string connection) public DbSet Users { get; set; } public DbSet Editors { get; set; } public DbSet Administrators { get; set; } - + [DbFunction("BloggingContext", "ClrStoredAddFunction")] public static int StoredAddFunction(int val1, int val2) { @@ -191,7 +193,7 @@ public IQueryable GetBlogsByName(string name) return ((IObjectContextAdapter)this).ObjectContext.CreateQuery( $"[GetBlogsByName](@Name)", nameParameter); } - + private static DbCompiledModel CreateModel(NpgsqlConnection connection) { var dbModelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest); @@ -211,6 +213,9 @@ private static DbCompiledModel CreateModel(NpgsqlConnection connection) var dbModel = dbModelBuilder.Build(connection); var edmType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32); + //these parameter types need to match both the database method and the C# method for EF to link + var edmStringType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String); + var addFunc = EdmFunction.Create( "ClrStoredAddFunction", "BloggingContext", diff --git a/EF6.PG/EF6.PG.csproj b/EF6.PG/EF6.PG.csproj index 32fb4d7..c319037 100644 --- a/EF6.PG/EF6.PG.csproj +++ b/EF6.PG/EF6.PG.csproj @@ -5,7 +5,7 @@ Copyright 2019 © The Npgsql Development Team Npgsql npgsql postgresql postgres data database entity framework ef orm - 6.4.1 + 6.4.3 latest net45;net461;netstandard21 true @@ -23,6 +23,8 @@ git git://github.com/npgsql/EntityFramework6.Npgsql true + SureWx.EntityFramework6.Npgsql + 6.4.3 diff --git a/EF6.PG/NpgsqlAggregateFunctions.cs b/EF6.PG/NpgsqlAggregateFunctions.cs new file mode 100644 index 0000000..4c7076c --- /dev/null +++ b/EF6.PG/NpgsqlAggregateFunctions.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Diagnostics.CodeAnalysis; + +namespace Npgsql +{ + /// + /// Use this class in LINQ queries to emit aggregate functions. + /// + [SuppressMessage("ReSharper", "UnusedParameter.Global")] + public static class NpgsqlAggregateFunctions + { + /// + /// Concatenate strings + /// + [DbFunction("NpgsqlAggregateFunctions", "StringAgg")] + public static string StringAgg(this IEnumerable source) + => throw new NotSupportedException(); + } +} diff --git a/EF6.PG/NpgsqlProviderManifest.cs b/EF6.PG/NpgsqlProviderManifest.cs index 3d7e4c3..8f9871e 100644 --- a/EF6.PG/NpgsqlProviderManifest.cs +++ b/EF6.PG/NpgsqlProviderManifest.cs @@ -325,9 +325,31 @@ public override ReadOnlyCollection GetStoreFunctions() .Select(x => new { Method = x, DbFunction = x.GetCustomAttribute() }) .Where(x => x.DbFunction != null) .Select(x => CreateComposableEdmFunction(x.Method, x.DbFunction)) + .Union(new [] + { + EdmFunction.Create("StringAgg", "NpgsqlAggregateFunctions", DataSpace.SSpace, new EdmFunctionPayload + { + ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, + IsComposable = true, + IsAggregate = true, + Schema = "", + IsFromProviderManifest = true, + IsBuiltIn = true, + StoreFunctionName = "string_agg", + ReturnParameters = new[] + { + FunctionParameter.Create("ReturnType", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.ReturnValue) + }, + Parameters = new[] + { + FunctionParameter.Create("input", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String).GetEdmPrimitiveType().GetCollectionType(), ParameterMode.In), + //FunctionParameter.Create("separator", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.In), + }, + }, null), + }) .ToList() .AsReadOnly(); - + static EdmFunction CreateComposableEdmFunction([NotNull] MethodInfo method, [NotNull] DbFunctionAttribute dbFunctionInfo) { if (method == null) diff --git a/EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml b/EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..527cc10 --- /dev/null +++ b/EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + Debug + Any CPU + bin\Release\net45\publish\ + FileSystem + net45 + + \ No newline at end of file diff --git a/EF6.PG/SqlGenerators/SqlBaseGenerator.cs b/EF6.PG/SqlGenerators/SqlBaseGenerator.cs index 6caf918..6a0601d 100644 --- a/EF6.PG/SqlGenerators/SqlBaseGenerator.cs +++ b/EF6.PG/SqlGenerators/SqlBaseGenerator.cs @@ -936,6 +936,40 @@ VisitedExpression VisitFunction(DbFunctionAggregate functionAggregate) aggregate.AddArgument(aggregateArg); return new CastExpression(aggregate, GetDbType(functionAggregate.ResultType.EdmType)); } + + if (functionAggregate.Function.NamespaceName == "NpgsqlAggregateFunctions") + { + FunctionExpression aggregate; + try + { + aggregate = new FunctionExpression(functionAggregate.Function.StoreFunctionNameAttribute); + } + catch (KeyNotFoundException) + { + throw new NotSupportedException(); + } + Debug.Assert(functionAggregate.Arguments.Count == 1); + VisitedExpression aggregateArg; + if (functionAggregate.Distinct) + { + aggregateArg = new LiteralExpression("DISTINCT "); + ((LiteralExpression)aggregateArg).Append(functionAggregate.Arguments[0].Accept(this)); + } + else + { + aggregateArg = functionAggregate.Arguments[0].Accept(this); + } + aggregate.AddArgument(aggregateArg); + + if (functionAggregate.Function.Name == "StringAgg") + { + // HACK add second argument, since EF doesn't support more than one argument for aggregate functions + aggregate.AddArgument(new ConstantExpression(", ", functionAggregate.ResultType)); + } + + return new CastExpression(aggregate, GetDbType(functionAggregate.ResultType.EdmType)); + } + throw new NotSupportedException(); } diff --git a/EntityFramework6.Npgsql.sln b/EntityFramework6.Npgsql.sln index d04e93d..cd09605 100644 --- a/EntityFramework6.Npgsql.sln +++ b/EntityFramework6.Npgsql.sln @@ -1,6 +1,7 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2035 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31105.61 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4A5A60DD-41B6-40BF-B677-227A921ECCC8}" ProjectSection(SolutionItems) = preProject From c4c5299f56d67ee3930dcb0b285aca4ffdfb388a Mon Sep 17 00:00:00 2001 From: Jonathan Amend Date: Tue, 14 Jan 2025 14:33:39 -0500 Subject: [PATCH 5/5] Import upstream changes, drop Postgis support, drop .NET Framework support, support Npgsql 7.x --- Directory.Build.targets | 17 +- EF6.PG.Tests/EF6.PG.Tests.csproj | 3 +- EF6.PG.Tests/NLogLoggingProvider.cs | 61 - EF6.PG.Tests/Spatial/PostgisServiceTests.cs | 1337 ----------------- EF6.PG.Tests/Support/AssemblySetup.cs | 5 - EF6.PG.Tests/Support/TestBase.cs | 1 - EF6.PG/EF6.PG.csproj | 23 +- EF6.PG/NpgsqlServices.cs | 8 +- EF6.PG/NpgsqlTypes/NpgsqlDate.cs | 481 ++++++ EF6.PG/NpgsqlTypes/NpgsqlDateTime.cs | 909 +++++++++++ EF6.PG/NpgsqlTypes/NpgsqlTimeSpan.cs | 510 +++++++ .../PublishProfiles/FolderProfile.pubxml | 4 +- EF6.PG/Spatial/PostgisDataReader.cs | 238 --- EF6.PG/Spatial/PostgisServices.cs | 1283 ---------------- EF6.PG/SqlGenerators/SqlSelectGenerator.cs | 38 +- EF6.PG/content/net45/App.config.transform | 11 - EF6.PG/content/net45/Web.config.transform | 11 - EF6.PG/content/net461/App.config.transform | 11 - EF6.PG/content/net461/Web.config.transform | 11 - 19 files changed, 1933 insertions(+), 3029 deletions(-) delete mode 100644 EF6.PG.Tests/NLogLoggingProvider.cs delete mode 100644 EF6.PG.Tests/Spatial/PostgisServiceTests.cs create mode 100644 EF6.PG/NpgsqlTypes/NpgsqlDate.cs create mode 100644 EF6.PG/NpgsqlTypes/NpgsqlDateTime.cs create mode 100644 EF6.PG/NpgsqlTypes/NpgsqlTimeSpan.cs delete mode 100644 EF6.PG/Spatial/PostgisDataReader.cs delete mode 100644 EF6.PG/Spatial/PostgisServices.cs delete mode 100644 EF6.PG/content/net45/App.config.transform delete mode 100644 EF6.PG/content/net45/Web.config.transform delete mode 100644 EF6.PG/content/net461/App.config.transform delete mode 100644 EF6.PG/content/net461/Web.config.transform diff --git a/Directory.Build.targets b/Directory.Build.targets index d3fa71d..d771697 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,20 +1,7 @@ - - - - - - - - - - - - - + + diff --git a/EF6.PG.Tests/EF6.PG.Tests.csproj b/EF6.PG.Tests/EF6.PG.Tests.csproj index 5f9b005..69f536e 100644 --- a/EF6.PG.Tests/EF6.PG.Tests.csproj +++ b/EF6.PG.Tests/EF6.PG.Tests.csproj @@ -1,7 +1,7 @@  latest - net45;netcoreapp3.1 + net6.0 EntityFramework6.Npgsql.Tests EntityFramework6.Npgsql.Tests @@ -11,7 +11,6 @@ - diff --git a/EF6.PG.Tests/NLogLoggingProvider.cs b/EF6.PG.Tests/NLogLoggingProvider.cs deleted file mode 100644 index 509a7f3..0000000 --- a/EF6.PG.Tests/NLogLoggingProvider.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using NLog; -using Npgsql.Logging; - -namespace EntityFramework6.Npgsql.Tests -{ - class NLogLoggingProvider : INpgsqlLoggingProvider - { - public NpgsqlLogger CreateLogger(string name) - - { - return new NLogLogger(name); - } - } - - class NLogLogger : NpgsqlLogger - { - readonly Logger _log; - - internal NLogLogger(string name) - { - _log = LogManager.GetLogger(name); - } - - public override bool IsEnabled(NpgsqlLogLevel level) - { - return _log.IsEnabled(ToNLogLogLevel(level)); - } - - public override void Log(NpgsqlLogLevel level, int connectorId, string msg, Exception exception = null) - { - var ev = new LogEventInfo(ToNLogLogLevel(level), "", msg); - if (exception != null) - ev.Exception = exception; - if (connectorId != 0) - ev.Properties["ConnectorId"] = connectorId; - _log.Log(ev); - } - - static LogLevel ToNLogLogLevel(NpgsqlLogLevel level) - { - switch (level) - { - case NpgsqlLogLevel.Trace: - return LogLevel.Trace; - case NpgsqlLogLevel.Debug: - return LogLevel.Debug; - case NpgsqlLogLevel.Info: - return LogLevel.Info; - case NpgsqlLogLevel.Warn: - return LogLevel.Warn; - case NpgsqlLogLevel.Error: - return LogLevel.Error; - case NpgsqlLogLevel.Fatal: - return LogLevel.Fatal; - default: - throw new ArgumentOutOfRangeException("level"); - } - } - } -} diff --git a/EF6.PG.Tests/Spatial/PostgisServiceTests.cs b/EF6.PG.Tests/Spatial/PostgisServiceTests.cs deleted file mode 100644 index e4fa6bc..0000000 --- a/EF6.PG.Tests/Spatial/PostgisServiceTests.cs +++ /dev/null @@ -1,1337 +0,0 @@ -using System; -using Npgsql; -using Npgsql.LegacyPostgis; -using Npgsql.Spatial; -using NUnit.Framework; -// ReSharper disable RedundantExplicitArrayCreation - -namespace EntityFramework6.Npgsql.Tests.Spatial -{ - public class PostgisServiceTests : TestBase - { - [Test] - public void AsBinaryTest() - { - var p = new PostgisPoint(1D, 1D); - var svcs = CreatePostgisServices(); - svcs.AsBinary(svcs.GeometryFromProviderValue(p)); - } - - [Test] - public void AsBinaryTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void AsGmlTest() - { - var p = new PostgisPoint(1D, 1D); - var svcs = CreatePostgisServices(); - svcs.AsGml(svcs.GeometryFromProviderValue(p)); - } - - [Test] - public void AsGmlTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void AsTextTest() - { - var p = new PostgisPoint(1D, 1D); - var svcs = CreatePostgisServices(); - svcs.AsText(svcs.GeometryFromProviderValue(p)); - } - - [Test] - public void AsTextTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void BufferTest() - { - var p = new PostgisPoint(1D, 1D); - var svcs = CreatePostgisServices(); - svcs.Buffer(svcs.GeometryFromProviderValue(p), 19D); - } - - [Test] - public void BufferTest1() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void ContainsTest() - { - var p = new PostgisPoint(1D, 1D); - var svcs = CreatePostgisServices(); - var pol = new PostgisPolygon(new Coordinate2D[1][] - {new Coordinate2D[5] - { - new Coordinate2D(0D,0D), - new Coordinate2D(5D,0D), - new Coordinate2D(5D,5D), - new Coordinate2D(0D,5D), - new Coordinate2D(0D,0D) - } - }); - Assert.True(svcs.Contains(svcs.GeometryFromProviderValue(pol), svcs.GeometryFromProviderValue(p))); - } - - [Test] - public void CreateProviderValueTest() - { - var svcs = CreatePostgisServices(); - svcs.CreateProviderValue( - svcs.CreateWellKnownValue(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D)))); - } - - [Test] - public void CreateProviderValueTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void CreateWellKnownValueTest() - { - var svcs = CreatePostgisServices(); - svcs.CreateWellKnownValue(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D))); - } - - [Test] - public void CreateWellKnownValueTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void CrossesTest() - { - var svcs = CreatePostgisServices(); - svcs.Crosses(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D)), - svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D))); - } - - [Test] - public void DifferenceTest() - { - var svcs = CreatePostgisServices(); - svcs.Difference(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D)), - svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D))); - } - - [Test] - public void DifferenceTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void DisjointTest() - { - var svcs = CreatePostgisServices(); - svcs.Disjoint(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D)), - svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D))); - } - - [Test] - public void DisjointTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void DistanceTest() - { - var svcs = CreatePostgisServices(); - svcs.Distance(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D)), - svcs.GeometryFromProviderValue(new PostgisPoint(1D, 1D))); - } - - [Test] - public void DistanceTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void ElementAtTest() - { - var svcs = CreatePostgisServices(); - svcs.ElementAt(svcs.GeometryFromProviderValue( - new PostgisGeometryCollection(new PostgisGeometry[1] { new PostgisPoint(0D, 0D) })), - 1); - } - - [Test] - public void ElementAtTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyCollectionFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyCollectionFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyFromBinaryTest1() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyFromGmlTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyFromGmlTest1() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyFromProviderValueTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyFromTextTest1() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyLineFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyLineFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyMultiLineFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyMultiLineFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyMultiPointFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyMultiPointFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyMultiPolygonFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyMultiPolygonFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyPointFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyPointFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyPolygonFromBinaryTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeographyPolygonFromTextTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeometryCollectionFromBinaryTest() - { - var svcs = CreatePostgisServices(); - try - { - var b = svcs.AsBinary(svcs.GeometryFromProviderValue(new PostgisGeometryCollection - (new PostgisGeometry[] { new PostgisPoint(0D, 0D) }))); - svcs.GeometryCollectionFromBinary(b, 1); - } - catch (NotImplementedException) - { - Assert.Ignore("not implemented"); - } - } - - [Test] - public void GeometryCollectionFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue(new PostgisGeometryCollection - (new PostgisGeometry[] { new PostgisPoint(0D, 0D) }))); - svcs.GeometryCollectionFromText(b, 1); - } - - [Test] - public void GeometryFromBinaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsBinary(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D))); - svcs.GeometryFromBinary(b, 1); - } - - [Test] - public void GeometryFromBinaryTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeometryFromGmlTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D))); - svcs.GeometryCollectionFromText(b, 1); - } - - [Test] - public void GeometryFromGmlTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeometryFromProviderValueTest() - { - var svcs = CreatePostgisServices(); - svcs.GeometryFromProviderValue(new PostgisPoint(0d, 0d)); - } - - [Test] - public void GeometryFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue(new PostgisPoint(0D, 0D))); - svcs.GeometryCollectionFromText(b, 1); - } - - [Test] - public void GeometryFromTextTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GeometryLineFromBinaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsBinary(svcs.GeometryFromProviderValue( - new PostgisLineString(new Coordinate2D[] { new Coordinate2D(0D, 0D), - new Coordinate2D(0d,0d) }))); - svcs.GeometryLineFromBinary(b, 1); - } - - [Test] - public void GeometryLineFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue( - new PostgisLineString(new Coordinate2D[] { new Coordinate2D(0D, 0D), - new Coordinate2D(0d,0d) }))); - svcs.GeometryLineFromText(b, 1); - } - - [Test] - public void GeometryMultiLineFromBinaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsBinary(svcs.GeometryFromProviderValue( - new PostgisMultiLineString( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,0d) - } - }))); - svcs.GeometryMultiLineFromBinary(b, 1); - } - - [Test] - public void GeometryMultiLineFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue( - new PostgisMultiLineString( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,0d) - } - }))); - svcs.GeometryMultiLineFromText(b, 1); - } - - [Test] - public void GeometryMultiPointFromBinaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsBinary(svcs.GeometryFromProviderValue( - new PostgisMultiPoint( - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,0d) - } - ))); - svcs.GeometryMultiPointFromBinary(b, 1); - } - - [Test] - public void GeometryMultiPointFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue( - new PostgisMultiPoint( - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,0d) - } - ))); - svcs.GeometryMultiPointFromText(b, 1); - } - - [Test] - public void GeometryMultiPolygonFromBinaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsBinary(svcs.GeometryFromProviderValue( - new PostgisMultiPolygon(new PostgisPolygon[] - {new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - } - ) - } - ))); - svcs.GeometryMultiPolygonFromBinary(b, 1); - } - - [Test] - public void GeometryMultiPolygonFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue( - new PostgisMultiPolygon(new PostgisPolygon[] - {new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - } - ) - } - ))); - svcs.GeometryMultiPolygonFromText(b, 1); - } - - [Test] - public void GeometryPointFromBinaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsBinary(svcs.GeometryFromProviderValue(new PostgisPoint(1D, 1D))); - svcs.GeometryPointFromBinary(b, 1); - } - - [Test] - public void GeometryPointFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue(new PostgisPoint(1D, 1D))); - svcs.GeometryPointFromText(b, 1); - } - - [Test] - public void GeometryPolygonFromBinaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsBinary(svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - }))); - svcs.GeometryPolygonFromBinary(b, 1); - } - - [Test] - public void GeometryPolygonFromTextTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.AsText(svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - }))); - svcs.GeometryPolygonFromText(b, 1); - } - - [Test] - public void GetAreaTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetArea(b); - } - - [Test] - public void GetAreaTestGeom() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetBoundaryTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetBoundary(b); - } - - [Test] - public void GetCentroidTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetCentroid(b); - } - - [Test] - public void GetConvexHullTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetConvexHull(b); - } - - [Test] - public void GetCoordinateSystemIdTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - }) - { SRID = 3742 }); - svcs.GetCoordinateSystemId(b); - } - - [Test] - public void GetCoordinateSystemIdTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetDimensionTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetDimension(b); - } - - [Test] - public void GetDimensionTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetElementCountTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisGeometryCollection( - new PostgisGeometry[] { - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - }) - }) - ); - svcs.GetElementCount(b); - } - - [Test] - public void GetElementCountTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetElevationTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetElevationTest1() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetEndPointTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetEndPoint(b); - } - - [Test] - public void GetEndPointTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetEnvelopeTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetEnvelope(b); - } - - [Test] - public void GetExteriorRingTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetExteriorRing(b); - } - - [Test] - public void GetInteriorRingCountTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetInteriorRingCount(b); - } - - [Test] - public void GetIsClosedTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetIsClosed(b); - } - - [Test] - public void GetIsClosedTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetIsEmptyTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetIsEmpty(b); - } - - [Test] - public void GetIsEmptyTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetIsRingTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisLineString( - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - })); - svcs.GetIsRing(b); - } - - [Test] - public void GetIsSimpleTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetIsSimple(b); - } - - [Test] - public void GetIsValidTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetIsSimple(b); - } - - [Test] - public void GetLatitudeTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetLengthTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetLength(b); - } - - [Test] - public void GetLengthTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetLongitudeTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetMeasureTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetMeasureTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetPointCountTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetPointCount(b); - } - - [Test] - public void GetPointCountTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetPointOnSurfaceTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetPointOnSurface(b); - } - - [Test] - public void GetSpatialTypeNameTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - Assert.IsTrue(svcs.GetSpatialTypeName(b).ToLower() == "polygon"); - } - - [Test] - public void GetSpatialTypeNameTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetStartPointTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.GetStartPoint(b); - } - - [Test] - public void GetStartPointTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void GetXCoordinateTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPoint(0D, 0D)); - svcs.GetXCoordinate(b); - } - - [Test] - public void GetYCoordinateTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPoint(0D, 0D)); - svcs.GetYCoordinate(b); - } - - [Test] - public void InteriorRingAtTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - ,new Coordinate2D[] { - new Coordinate2D(0.5D, 0.5D), - new Coordinate2D(0.5d,0.8d), - new Coordinate2D(0.8d,0.8d), - new Coordinate2D(0.8d,0.5d), - new Coordinate2D(0.5d,0.5d) - } - })); - svcs.InteriorRingAt(b, 2); - } - - [Test] - public void IntersectionTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.Intersection(b, b); - } - - [Test] - public void IntersectionTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void IntersectsTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.Intersection(b, b); - } - - [Test] - public void IntersectsTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void OverlapsTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.Overlaps(b, b); - } - - [Test] - public void PointAtTest() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void PointAtTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void RelateTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.Relate(b, b, "0FFFFF212"); - } - - [Test] - public void SpatialEqualsTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.SpatialEquals(b, b); - } - - [Test] - public void SpatialEqualsTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void SymmetricDifferenceTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.SymmetricDifference(b, b); - } - - [Test] - public void SymmetricDifferenceTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void TouchesTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.Touches(b, b); - } - - [Test] - public void UnionTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.Union(b, b); - } - - [Test] - public void UnionTestGeog() - { - Assert.Ignore("not implemented"); - } - - [Test] - public void WithinTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - svcs.Within(b, b); - } - - [Test] - public void InstanceTest() - { - var svcs = CreatePostgisServices(); - var b = svcs.GeometryFromProviderValue( - new PostgisPolygon( - new Coordinate2D[][] { - new Coordinate2D[] { - new Coordinate2D(0D, 0D), - new Coordinate2D(0d,1d), - new Coordinate2D(1d,1d), - new Coordinate2D(1d,0d), - new Coordinate2D(0d,0d) - } - })); - var x = b.Area; - } - - #region Support - - // ReSharper disable once InconsistentNaming - PostgisServices CreatePostgisServices() - { - var conn = new NpgsqlConnection(ConnectionString); - conn.Open(); - - var svcs = new PostgisServices(); - svcs.SetConnection(conn); - return svcs; - } - - [OneTimeSetUp] - public new void TestFixtureSetup() - { - using (var context = new BloggingContext(ConnectionString)) - { - if (!context.Database.Exists()) - context.Database.Create(); - } - - using (var conn = new NpgsqlConnection(ConnectionString)) - { - NpgsqlConnection.GlobalTypeMapper.UseLegacyPostgis(); - conn.Open(); - using (var cmd = new NpgsqlCommand("CREATE EXTENSION IF NOT EXISTS postgis", conn)) - cmd.ExecuteNonQuery(); - - // Need to also have Npgsql reload the types from the database - conn.ReloadTypes(); - NpgsqlConnection.ClearPool(conn); - } - } - - #endregion Support - } -} diff --git a/EF6.PG.Tests/Support/AssemblySetup.cs b/EF6.PG.Tests/Support/AssemblySetup.cs index e503497..ebc05ef 100644 --- a/EF6.PG.Tests/Support/AssemblySetup.cs +++ b/EF6.PG.Tests/Support/AssemblySetup.cs @@ -2,8 +2,6 @@ using NLog.Config; using NLog.Targets; using NUnit.Framework; -using Npgsql.Logging; -using EntityFramework6.Npgsql.Tests; using EntityFramework6.Npgsql.Tests.Support; // ReSharper disable CheckNamespace @@ -22,9 +20,6 @@ public void RegisterDbProvider() config.LoggingRules.Add(rule); NLog.LogManager.Configuration = config; - NpgsqlLogManager.Provider = new NLogLoggingProvider(); - NpgsqlLogManager.IsParameterLoggingEnabled = true; - DbConfiguration.SetConfiguration(new TestDbConfiguration()); } } diff --git a/EF6.PG.Tests/Support/TestBase.cs b/EF6.PG.Tests/Support/TestBase.cs index cf23265..2dd79b8 100644 --- a/EF6.PG.Tests/Support/TestBase.cs +++ b/EF6.PG.Tests/Support/TestBase.cs @@ -3,7 +3,6 @@ using NLog.Targets; using NLog; using Npgsql; -using Npgsql.Logging; using NUnit.Framework; diff --git a/EF6.PG/EF6.PG.csproj b/EF6.PG/EF6.PG.csproj index a28a659..f93b066 100644 --- a/EF6.PG/EF6.PG.csproj +++ b/EF6.PG/EF6.PG.csproj @@ -5,9 +5,9 @@ Copyright 2020 © The Npgsql Development Team Npgsql npgsql postgresql postgres data database entity framework ef orm - 6.4.3 + 6.4.4 latest - net45;net461;netstandard21 + net6.0 true CS1591 true @@ -24,34 +24,19 @@ git://github.com/npgsql/EntityFramework6.Npgsql true SureWx.EntityFramework6.Npgsql - 6.4.3 + 6.4.4 - - - - - - - - - - - - - - - - + diff --git a/EF6.PG/NpgsqlServices.cs b/EF6.PG/NpgsqlServices.cs index 4108f76..15bf99c 100644 --- a/EF6.PG/NpgsqlServices.cs +++ b/EF6.PG/NpgsqlServices.cs @@ -145,12 +145,6 @@ protected override void DbCreateDatabase([NotNull] DbConnection connection, int? sb.Append("CREATE DATABASE \""); sb.Append(connection.Database); sb.Append("\""); - if (conn.Settings.EntityTemplateDatabase != null) - { - sb.Append(" TEMPLATE \""); - sb.Append(conn.Settings.EntityTemplateDatabase); - sb.Append("\""); - } using (var command = new NpgsqlCommand(sb.ToString(), conn)) command.ExecuteNonQuery(); @@ -172,7 +166,7 @@ static void UsingPostgresDbConnection(NpgsqlConnection connection, Action, IComparable, IComparable, + IComparer, IComparer +{ + //Number of days since January 1st CE (January 1st EV). 1 Jan 1 CE = 0, 2 Jan 1 CE = 1, 31 Dec 1 BCE = -1, etc. + readonly int _daysSinceEra; + readonly InternalType _type; + + #region Constants + + static readonly int[] CommonYearDays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; + static readonly int[] LeapYearDays = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; + static readonly int[] CommonYearMaxes = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + static readonly int[] LeapYearMaxes = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + /// + /// Represents the date 1970-01-01 + /// + public static readonly NpgsqlDate Epoch = new(1970, 1, 1); + + /// + /// Represents the date 0001-01-01 + /// + public static readonly NpgsqlDate Era = new(0); + + public const int MaxYear = 5874897; + public const int MinYear = -4714; + public static readonly NpgsqlDate MaxCalculableValue = new(MaxYear, 12, 31); + public static readonly NpgsqlDate MinCalculableValue = new(MinYear, 11, 24); + + public static readonly NpgsqlDate Infinity = new(InternalType.Infinity); + public static readonly NpgsqlDate NegativeInfinity = new(InternalType.NegativeInfinity); + + const int DaysInYear = 365; //Common years + const int DaysIn4Years = 4 * DaysInYear + 1; //Leap year every 4 years. + const int DaysInCentury = 25 * DaysIn4Years - 1; //Except no leap year every 100. + const int DaysIn4Centuries = 4 * DaysInCentury + 1; //Except leap year every 400. + + #endregion + + #region Constructors + + NpgsqlDate(InternalType type) + { + _type = type; + _daysSinceEra = 0; + } + + internal NpgsqlDate(int days) + { + _type = InternalType.Finite; + _daysSinceEra = days; + } + + public NpgsqlDate(DateTime dateTime) : this((int)(dateTime.Ticks / TimeSpan.TicksPerDay)) {} + + public NpgsqlDate(NpgsqlDate copyFrom) : this(copyFrom._daysSinceEra) {} + + public NpgsqlDate(int year, int month, int day) + { + _type = InternalType.Finite; + if (year == 0 || year < MinYear || year > MaxYear || month < 1 || month > 12 || day < 1 || + (day > (IsLeap(year) ? 366 : 365))) + { + throw new ArgumentOutOfRangeException(); + } + + _daysSinceEra = DaysForYears(year) + (IsLeap(year) ? LeapYearDays : CommonYearDays)[month - 1] + day - 1; + } + + #endregion + + #region String Conversions + + public override string ToString() + => _type switch + { + InternalType.Infinity => "infinity", + InternalType.NegativeInfinity => "-infinity", + //Format of yyyy-MM-dd with " BC" for BCE and optional " AD" for CE which we omit here. + _ => new StringBuilder(Math.Abs(Year).ToString("D4")) + .Append('-').Append(Month.ToString("D2")) + .Append('-').Append(Day.ToString("D2")) + .Append(_daysSinceEra < 0 ? " BC" : "").ToString() + }; + + public static NpgsqlDate Parse(string str) + { + + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + + if (str == "infinity") + return Infinity; + + if (str == "-infinity") + return NegativeInfinity; + + str = str.Trim(); + try { + var idx = str.IndexOf('-'); + if (idx == -1) { + throw new FormatException(); + } + var year = int.Parse(str.Substring(0, idx)); + var idxLast = idx + 1; + if ((idx = str.IndexOf('-', idxLast)) == -1) { + throw new FormatException(); + } + var month = int.Parse(str.Substring(idxLast, idx - idxLast)); + idxLast = idx + 1; + if ((idx = str.IndexOf(' ', idxLast)) == -1) { + idx = str.Length; + } + var day = int.Parse(str.Substring(idxLast, idx - idxLast)); + if (str.Contains("BC")) { + year = -year; + } + return new NpgsqlDate(year, month, day); + } catch (OverflowException) { + throw; + } catch (Exception) { + throw new FormatException(); + } + } + + public static bool TryParse(string str, out NpgsqlDate date) + { + try { + date = Parse(str); + return true; + } catch { + date = Era; + return false; + } + } + + #endregion + + #region Public Properties + + public static NpgsqlDate Now => new(DateTime.Now); + public static NpgsqlDate Today => Now; + public static NpgsqlDate Yesterday => Now.AddDays(-1); + public static NpgsqlDate Tomorrow => Now.AddDays(1); + + public int DayOfYear => _daysSinceEra - DaysForYears(Year) + 1; + + public int Year + { + get + { + var guess = (int)Math.Round(_daysSinceEra/365.2425); + var test = guess - 1; + while (DaysForYears(++test) <= _daysSinceEra) {} + return test - 1; + } + } + + public int Month + { + get + { + var i = 1; + var target = DayOfYear; + var array = IsLeapYear ? LeapYearDays : CommonYearDays; + while (target > array[i]) + { + ++i; + } + return i; + } + } + + public int Day => DayOfYear - (IsLeapYear ? LeapYearDays : CommonYearDays)[Month - 1]; + + public DayOfWeek DayOfWeek => (DayOfWeek) ((_daysSinceEra + 1)%7); + + internal int DaysSinceEra => _daysSinceEra; + + public bool IsLeapYear => IsLeap(Year); + + public bool IsInfinity => _type == InternalType.Infinity; + public bool IsNegativeInfinity => _type == InternalType.NegativeInfinity; + + public bool IsFinite + => _type switch { + InternalType.Finite => true, + InternalType.Infinity => false, + InternalType.NegativeInfinity => false, + _ => throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug.") + }; + + #endregion + + #region Internals + + static int DaysForYears(int years) + { + //Number of years after 1CE (0 for 1CE, -1 for 1BCE, 1 for 2CE). + var calcYear = years < 1 ? years : years - 1; + + return calcYear / 400 * DaysIn4Centuries //Blocks of 400 years with their leap and common years + + calcYear % 400 / 100 * DaysInCentury //Remaining blocks of 100 years with their leap and common years + + calcYear % 100 / 4 * DaysIn4Years //Remaining blocks of 4 years with their leap and common years + + calcYear % 4 * DaysInYear //Remaining years, all common + + (calcYear < 0 ? -1 : 0); //And 1BCE is leap. + } + + static bool IsLeap(int year) + { + //Every 4 years is a leap year + //Except every 100 years isn't a leap year. + //Except every 400 years is. + if (year < 1) + { + year = year + 1; + } + return (year%4 == 0) && ((year%100 != 0) || (year%400 == 0)); + } + + #endregion + + #region Arithmetic + + public NpgsqlDate AddDays(int days) + => _type switch + { + InternalType.Infinity => Infinity, + InternalType.NegativeInfinity => NegativeInfinity, + InternalType.Finite => new NpgsqlDate(_daysSinceEra + days), + _ => throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug.") + }; + + public NpgsqlDate AddYears(int years) + { + switch (_type) { + case InternalType.Infinity: + return Infinity; + case InternalType.NegativeInfinity: + return NegativeInfinity; + case InternalType.Finite: + break; + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug."); + } + + var newYear = Year + years; + if (newYear >= 0 && _daysSinceEra < 0) //cross 1CE/1BCE divide going up + { + ++newYear; + } + else if (newYear <= 0 && _daysSinceEra >= 0) //cross 1CE/1BCE divide going down + { + --newYear; + } + return new NpgsqlDate(newYear, Month, Day); + } + + public NpgsqlDate AddMonths(int months) + { + switch (_type) { + case InternalType.Infinity: + return Infinity; + case InternalType.NegativeInfinity: + return NegativeInfinity; + case InternalType.Finite: + break; + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug."); + } + + var newYear = Year; + var newMonth = Month + months; + + while (newMonth > 12) + { + newMonth -= 12; + newYear += 1; + } + while (newMonth < 1) + { + newMonth += 12; + newYear -= 1; + } + var maxDay = (IsLeap(newYear) ? LeapYearMaxes : CommonYearMaxes)[newMonth - 1]; + var newDay = Day > maxDay ? maxDay : Day; + return new NpgsqlDate(newYear, newMonth, newDay); + + } + + public NpgsqlDate Add(in NpgsqlTimeSpan interval) + { + switch (_type) { + case InternalType.Infinity: + return Infinity; + case InternalType.NegativeInfinity: + return NegativeInfinity; + case InternalType.Finite: + break; + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug."); + } + + return AddMonths(interval.Months).AddDays(interval.Days); + } + + internal NpgsqlDate Add(in NpgsqlTimeSpan interval, int carriedOverflow) + { + switch (_type) { + case InternalType.Infinity: + return Infinity; + case InternalType.NegativeInfinity: + return NegativeInfinity; + case InternalType.Finite: + break; + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug."); + } + + return AddMonths(interval.Months).AddDays(interval.Days + carriedOverflow); + } + + #endregion + + #region Comparison + + public int Compare(NpgsqlDate x, NpgsqlDate y) => x.CompareTo(y); + + public int Compare(object? x, object? y) + { + if (x == null) + { + return y == null ? 0 : -1; + } + if (y == null) + { + return 1; + } + if (!(x is IComparable) || !(y is IComparable)) + { + throw new ArgumentException(); + } + return ((IComparable) x).CompareTo(y); + } + + public bool Equals(NpgsqlDate other) + => _type switch + { + InternalType.Infinity => other._type == InternalType.Infinity, + InternalType.NegativeInfinity => other._type == InternalType.NegativeInfinity, + InternalType.Finite => other._type == InternalType.Finite && _daysSinceEra == other._daysSinceEra, + _ => false + }; + + public override bool Equals(object? obj) => obj is NpgsqlDate date && Equals(date); + + public int CompareTo(NpgsqlDate other) + => _type switch + { + InternalType.Infinity => other._type == InternalType.Infinity ? 0 : 1, + InternalType.NegativeInfinity => other._type == InternalType.NegativeInfinity ? 0 : -1, + _ => other._type switch + { + InternalType.Infinity => -1, + InternalType.NegativeInfinity => 1, + _ => _daysSinceEra.CompareTo(other._daysSinceEra) + } + }; + + public int CompareTo(object? o) + => o == null + ? 1 + : o is NpgsqlDate npgsqlDate + ? CompareTo(npgsqlDate) + : throw new ArgumentException(); + + public override int GetHashCode() => _daysSinceEra; + + #endregion + + #region Operators + + public static bool operator ==(NpgsqlDate x, NpgsqlDate y) => x.Equals(y); + public static bool operator !=(NpgsqlDate x, NpgsqlDate y) => !(x == y); + public static bool operator <(NpgsqlDate x, NpgsqlDate y) => x.CompareTo(y) < 0; + public static bool operator >(NpgsqlDate x, NpgsqlDate y) => x.CompareTo(y) > 0; + public static bool operator <=(NpgsqlDate x, NpgsqlDate y) => x.CompareTo(y) <= 0; + public static bool operator >=(NpgsqlDate x, NpgsqlDate y) => x.CompareTo(y) >= 0; + + public static DateTime ToDateTime(NpgsqlDate date) + { + switch (date._type) + { + case InternalType.Infinity: + case InternalType.NegativeInfinity: + throw new InvalidCastException("Infinity values can't be cast to DateTime"); + case InternalType.Finite: + try { return new DateTime(date._daysSinceEra * NpgsqlTimeSpan.TicksPerDay); } + catch { throw new InvalidCastException(); } + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {date._type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug."); + } + } + + public static explicit operator DateTime(NpgsqlDate date) => ToDateTime(date); + + public static NpgsqlDate ToNpgsqlDate(DateTime date) + => new((int)(date.Ticks / NpgsqlTimeSpan.TicksPerDay)); + + public static explicit operator NpgsqlDate(DateTime date) => ToNpgsqlDate(date); + + public static NpgsqlDate operator +(NpgsqlDate date, NpgsqlTimeSpan interval) + => date.Add(interval); + + public static NpgsqlDate operator +(NpgsqlTimeSpan interval, NpgsqlDate date) + => date.Add(interval); + + public static NpgsqlDate operator -(NpgsqlDate date, NpgsqlTimeSpan interval) + => date.Subtract(interval); + + public NpgsqlDate Subtract(in NpgsqlTimeSpan interval) => Add(-interval); + + public static NpgsqlTimeSpan operator -(NpgsqlDate dateX, NpgsqlDate dateY) + { + if (dateX._type != InternalType.Finite || dateY._type != InternalType.Finite) + throw new ArgumentException("Can't subtract infinity date values"); + + return new NpgsqlTimeSpan(0, dateX._daysSinceEra - dateY._daysSinceEra, 0); + } + + #endregion + +#if NET6_0_OR_GREATER + public NpgsqlDate(DateOnly date) : this(date.Year, date.Month, date.Day) {} + + public static DateOnly ToDateOnly(NpgsqlDate date) + { + switch (date._type) + { + case InternalType.Infinity: + case InternalType.NegativeInfinity: + throw new InvalidCastException("Infinity values can't be cast to DateTime"); + case InternalType.Finite: + try { return new DateOnly(date.Year, date.Month, date.Day); } + catch { throw new InvalidCastException(); } + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {date._type} of enum {nameof(NpgsqlDate)}.{nameof(InternalType)}. Please file a bug."); + } + } + + public static explicit operator DateOnly(NpgsqlDate date) => ToDateOnly(date); + + public static NpgsqlDate ToNpgsqlDate(DateOnly date) + => new(date.Year, date.Month, date.Day); + + public static explicit operator NpgsqlDate(DateOnly date) => ToNpgsqlDate(date); +#endif + + enum InternalType + { + Finite, + Infinity, + NegativeInfinity + } +} diff --git a/EF6.PG/NpgsqlTypes/NpgsqlDateTime.cs b/EF6.PG/NpgsqlTypes/NpgsqlDateTime.cs new file mode 100644 index 0000000..5d33dbc --- /dev/null +++ b/EF6.PG/NpgsqlTypes/NpgsqlDateTime.cs @@ -0,0 +1,909 @@ +#nullable enable +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Npgsql; + +// ReSharper disable once CheckNamespace +namespace NpgsqlTypes; + +/// +/// Represents the PostgreSQL interval datatype. +/// +/// PostgreSQL differs from .NET in how it's interval type doesn't assume 24 hours in a day +/// (to deal with 23- and 25-hour days caused by daylight savings adjustments) and has a concept +/// of months that doesn't exist in .NET's class. (Neither datatype +/// has any concessions for leap-seconds). +/// For most uses just casting to and from TimeSpan will work correctly — in particular, +/// the results of subtracting one or the PostgreSQL date, time and +/// timestamp types from another should be the same whether you do so in .NET or PostgreSQL — +/// but if the handling of days and months in PostgreSQL is important to your application then you +/// should use this class instead of . +/// If you don't know whether these differences are important to your application, they +/// probably arent! Just use and do not use this class directly ☺ +/// To avoid forcing unnecessary provider-specific concerns on users who need not be concerned +/// with them a call to on a field containing an +/// value will return a rather than an +/// . +/// +/// +/// +/// +/// +//[Obsolete( +// "For values outside the range of TimeSpan, consider using NodaTime (range -9998 to 9999), or read the value as an NpgsqlInterval. " + +// "See https://www.npgsql.org/doc/types/datetime.html for more information.")] +[Serializable] +public readonly struct NpgsqlTimeSpan : IComparable, IComparer, IEquatable, IComparable, + IComparer +{ + #region Constants + + /// + /// Represents the number of ticks (100ns periods) in one microsecond. This field is constant. + /// + public const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000; + + /// + /// Represents the number of ticks (100ns periods) in one millisecond. This field is constant. + /// + public const long TicksPerMillsecond = TimeSpan.TicksPerMillisecond; + + /// + /// Represents the number of ticks (100ns periods) in one second. This field is constant. + /// + public const long TicksPerSecond = TimeSpan.TicksPerSecond; + + /// + /// Represents the number of ticks (100ns periods) in one minute. This field is constant. + /// + public const long TicksPerMinute = TimeSpan.TicksPerMinute; + + /// + /// Represents the number of ticks (100ns periods) in one hour. This field is constant. + /// + public const long TicksPerHour = TimeSpan.TicksPerHour; + + /// + /// Represents the number of ticks (100ns periods) in one day. This field is constant. + /// + public const long TicksPerDay = TimeSpan.TicksPerDay; + + /// + /// Represents the number of hours in one day (assuming no daylight savings adjustments). This field is constant. + /// + public const int HoursPerDay = 24; + + /// + /// Represents the number of days assumed in one month if month justification or unjustifcation is performed. + /// This is set to 30 for consistency with PostgreSQL. Note that this is means that month adjustments cause + /// a year to be taken as 30 × 12 = 360 rather than 356/366 days. + /// + public const int DaysPerMonth = 30; + + /// + /// Represents the number of ticks (100ns periods) in one day, assuming 30 days per month. + /// + public const long TicksPerMonth = TicksPerDay * DaysPerMonth; + + /// + /// Represents the number of months in a year. This field is constant. + /// + public const int MonthsPerYear = 12; + + /// + /// Represents the maximum . This field is read-only. + /// + public static readonly NpgsqlTimeSpan MaxValue = new(long.MaxValue); + + /// + /// Represents the minimum . This field is read-only. + /// + public static readonly NpgsqlTimeSpan MinValue = new(long.MinValue); + + /// + /// Represents the zero . This field is read-only. + /// + public static readonly NpgsqlTimeSpan Zero = new(0); + + #endregion + + readonly int _months; + readonly int _days; + readonly long _ticks; + + #region Constructors + + /// + /// Initializes a new to the specified number of ticks. + /// + /// A time period expressed in 100ns units. + public NpgsqlTimeSpan(long ticks) + : this(new TimeSpan(ticks)) + { + } + + /// + /// Initializes a new to hold the same time as a + /// + /// A time period expressed in a + public NpgsqlTimeSpan(TimeSpan timespan) + : this(0, timespan.Days, timespan.Ticks - (TicksPerDay * timespan.Days)) + { + } + + /// + /// Initializes a new to the specified number of months, days + /// & ticks. + /// + /// Number of months. + /// Number of days. + /// Number of 100ns units. + public NpgsqlTimeSpan(int months, int days, long ticks) + { + _months = months; + _days = days; + _ticks = ticks; + } + + /// + /// Initializes a new to the specified number of + /// days, hours, minutes & seconds. + /// + /// Number of days. + /// Number of hours. + /// Number of minutes. + /// Number of seconds. + public NpgsqlTimeSpan(int days, int hours, int minutes, int seconds) + : this(0, days, new TimeSpan(hours, minutes, seconds).Ticks) + { + } + + /// + /// Initializes a new to the specified number of + /// days, hours, minutes, seconds & milliseconds. + /// + /// Number of days. + /// Number of hours. + /// Number of minutes. + /// Number of seconds. + /// Number of milliseconds. + public NpgsqlTimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) + : this(0, days, new TimeSpan(0, hours, minutes, seconds, milliseconds).Ticks) + { + } + + /// + /// Initializes a new to the specified number of + /// months, days, hours, minutes, seconds & milliseconds. + /// + /// Number of months. + /// Number of days. + /// Number of hours. + /// Number of minutes. + /// Number of seconds. + /// Number of milliseconds. + public NpgsqlTimeSpan(int months, int days, int hours, int minutes, int seconds, int milliseconds) + : this(months, days, new TimeSpan(0, hours, minutes, seconds, milliseconds).Ticks) + { + } + + /// + /// Initializes a new to the specified number of + /// years, months, days, hours, minutes, seconds & milliseconds. + /// Years are calculated exactly equivalent to 12 months. + /// + /// Number of years. + /// Number of months. + /// Number of days. + /// Number of hours. + /// Number of minutes. + /// Number of seconds. + /// Number of milliseconds. + public NpgsqlTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds) + : this(years * 12 + months, days, new TimeSpan(0, hours, minutes, seconds, milliseconds).Ticks) + { + } + + #endregion + + #region Whole Parts + + /// + /// The total number of ticks(100ns units) contained. This is the resolution of the + /// type. This ignores the number of days and + /// months held. If you want them included use first. + /// The resolution of the PostgreSQL + /// interval type is by default 1µs = 1,000 ns. It may be smaller as follows: + /// + /// + /// interval(0) + /// resolution of 1s (1 second) + /// + /// + /// interval(1) + /// resolution of 100ms = 0.1s (100 milliseconds) + /// + /// + /// interval(2) + /// resolution of 10ms = 0.01s (10 milliseconds) + /// + /// + /// interval(3) + /// resolution of 1ms = 0.001s (1 millisecond) + /// + /// + /// interval(4) + /// resolution of 100µs = 0.0001s (100 microseconds) + /// + /// + /// interval(5) + /// resolution of 10µs = 0.00001s (10 microseconds) + /// + /// + /// interval(6) or interval + /// resolution of 1µs = 0.000001s (1 microsecond) + /// + /// + /// As such, if the 100-nanosecond resolution is significant to an application, a PostgreSQL interval will + /// not suffice for those purposes. + /// In more frequent cases though, the resolution of the interval suffices. + /// will always suffice to handle the resolution of any interval value, and upon + /// writing to the database, will be rounded to the resolution used. + /// + /// The number of ticks in the instance. + /// + public long Ticks => _ticks; + + /// + /// Gets the number of whole microseconds held in the instance. + /// An in the range [-999999, 999999]. + /// + public int Microseconds => (int)((_ticks / 10) % 1000000); + + /// + /// Gets the number of whole milliseconds held in the instance. + /// An in the range [-999, 999]. + /// + public int Milliseconds => (int)((_ticks / TicksPerMillsecond) % 1000); + + /// + /// Gets the number of whole seconds held in the instance. + /// An in the range [-59, 59]. + /// + public int Seconds => (int)((_ticks / TicksPerSecond) % 60); + + /// + /// Gets the number of whole minutes held in the instance. + /// An in the range [-59, 59]. + /// + public int Minutes => (int)((_ticks / TicksPerMinute) % 60); + + /// + /// Gets the number of whole hours held in the instance. + /// Note that this can be less than -23 or greater than 23 unless + /// has been used to produce this instance. + /// + public int Hours => (int)(_ticks / TicksPerHour); + + /// + /// Gets the number of days held in the instance. + /// Note that this does not pay attention to a time component with -24 or less hours or + /// 24 or more hours, unless has been called to produce this instance. + /// + public int Days => _days; + + /// + /// Gets the number of months held in the instance. + /// Note that this does not pay attention to a day component with -30 or less days or + /// 30 or more days, unless has been called to produce this instance. + /// + public int Months => _months; + + /// + /// Returns a representing the time component of the instance. + /// Note that this may have a value beyond the range ±23:59:59.9999999 unless + /// has been called to produce this instance. + /// + public TimeSpan Time => new(_ticks); + + #endregion + + #region Total Parts + + /// + /// The total number of ticks (100ns units) in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public long TotalTicks => Ticks + Days * TicksPerDay + Months * TicksPerMonth; + + /// + /// The total number of microseconds in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public double TotalMicroseconds => TotalTicks / 10d; + + /// + /// The total number of milliseconds in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public double TotalMilliseconds => TotalTicks / (double)TicksPerMillsecond; + + /// + /// The total number of seconds in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public double TotalSeconds => TotalTicks / (double)TicksPerSecond; + + /// + /// The total number of minutes in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public double TotalMinutes => TotalTicks / (double)TicksPerMinute; + + /// + /// The total number of hours in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public double TotalHours => TotalTicks / (double)TicksPerHour; + + /// + /// The total number of days in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public double TotalDays => TotalTicks / (double)TicksPerDay; + + /// + /// The total number of months in the instance, assuming 24 hours in each day and + /// 30 days in a month. + /// + public double TotalMonths => TotalTicks / (double)TicksPerMonth; + + #endregion + + #region Create From Part + + /// + /// Creates an from a number of ticks. + /// + /// The number of ticks (100ns units) in the interval. + /// A d with the given number of ticks. + public static NpgsqlTimeSpan FromTicks(long ticks) => new NpgsqlTimeSpan(ticks).Canonicalize(); + + /// + /// Creates an from a number of microseconds. + /// + /// The number of microseconds in the interval. + /// A d with the given number of microseconds. + public static NpgsqlTimeSpan FromMicroseconds(double micro) => FromTicks((long)(micro * TicksPerMicrosecond)); + + /// + /// Creates an from a number of milliseconds. + /// + /// The number of milliseconds in the interval. + /// A d with the given number of milliseconds. + public static NpgsqlTimeSpan FromMilliseconds(double milli) => FromTicks((long)(milli * TicksPerMillsecond)); + + /// + /// Creates an from a number of seconds. + /// + /// The number of seconds in the interval. + /// A d with the given number of seconds. + public static NpgsqlTimeSpan FromSeconds(double seconds) => FromTicks((long)(seconds * TicksPerSecond)); + + /// + /// Creates an from a number of minutes. + /// + /// The number of minutes in the interval. + /// A d with the given number of minutes. + public static NpgsqlTimeSpan FromMinutes(double minutes) => FromTicks((long)(minutes * TicksPerMinute)); + + /// + /// Creates an from a number of hours. + /// + /// The number of hours in the interval. + /// A d with the given number of hours. + public static NpgsqlTimeSpan FromHours(double hours) => FromTicks((long)(hours * TicksPerHour)); + + /// + /// Creates an from a number of days. + /// + /// The number of days in the interval. + /// A d with the given number of days. + public static NpgsqlTimeSpan FromDays(double days) => FromTicks((long)(days * TicksPerDay)); + + /// + /// Creates an from a number of months. + /// + /// The number of months in the interval. + /// A d with the given number of months. + public static NpgsqlTimeSpan FromMonths(double months) => FromTicks((long)(months * TicksPerMonth)); + + #endregion + + #region Arithmetic + + /// + /// Adds another interval to this instance and returns the result. + /// + /// An to add to this instance. + /// An whose values are the sums of the two instances. + public NpgsqlTimeSpan Add(in NpgsqlTimeSpan interval) + => new(Months + interval.Months, Days + interval.Days, Ticks + interval.Ticks); + + /// + /// Subtracts another interval from this instance and returns the result. + /// + /// An to subtract from this instance. + /// An whose values are the differences of the two instances. + public NpgsqlTimeSpan Subtract(in NpgsqlTimeSpan interval) + => new(Months - interval.Months, Days - interval.Days, Ticks - interval.Ticks); + + /// + /// Returns an whose value is the negated value of this instance. + /// + /// An whose value is the negated value of this instance. + public NpgsqlTimeSpan Negate() => new(-Months, -Days, -Ticks); + + /// + /// This absolute value of this instance. In the case of some, but not all, components being negative, + /// the rules used for justification are used to determine if the instance is positive or negative. + /// + /// An whose value is the absolute value of this instance. + public NpgsqlTimeSpan Duration() + => UnjustifyInterval().Ticks < 0 ? Negate() : this; + + #endregion + + #region Justification + + /// + /// Equivalent to PostgreSQL's justify_days function. + /// + /// An based on this one, but with any hours outside of the range [-23, 23] + /// converted into days. + public NpgsqlTimeSpan JustifyDays() + { + return new(Months, Days + (int)(Ticks / TicksPerDay), Ticks % TicksPerDay); + } + + /// + /// Opposite to PostgreSQL's justify_days function. + /// + /// An based on this one, but with any days converted to multiples of ±24hours. + public NpgsqlTimeSpan UnjustifyDays() + { + return new(Months, 0, Ticks + Days * TicksPerDay); + } + + /// + /// Equivalent to PostgreSQL's justify_months function. + /// + /// An based on this one, but with any days outside of the range [-30, 30] + /// converted into months. + public NpgsqlTimeSpan JustifyMonths() + { + return new(Months + Days / DaysPerMonth, Days % DaysPerMonth, Ticks); + } + + /// + /// Opposite to PostgreSQL's justify_months function. + /// + /// An based on this one, but with any months converted to multiples of ±30days. + public NpgsqlTimeSpan UnjustifyMonths() + { + return new(0, Days + Months * DaysPerMonth, Ticks); + } + + /// + /// Equivalent to PostgreSQL's justify_interval function. + /// + /// An based on this one, + /// but with any months converted to multiples of ±30days + /// and then with any days converted to multiples of ±24hours + public NpgsqlTimeSpan JustifyInterval() + { + return JustifyMonths().JustifyDays(); + } + + /// + /// Opposite to PostgreSQL's justify_interval function. + /// + /// An based on this one, but with any months converted to multiples of ±30days and then any days converted to multiples of ±24hours; + public NpgsqlTimeSpan UnjustifyInterval() + { + return new(Ticks + Days * TicksPerDay + Months * DaysPerMonth * TicksPerDay); + } + + /// + /// Produces a canonical NpgslInterval with 0 months and hours in the range of [-23, 23]. + /// + /// + /// While the fact that for many purposes, two different instances could be considered + /// equivalent (e.g. one with 2days, 3hours and one with 1day 27hours) there are different possible canonical forms. + /// + /// E.g. we could move all excess hours into days and all excess days into months and have the most readable form, + /// or we could move everything into the ticks and have the form that allows for the easiest arithmetic) the form + /// chosen has two important properties that make it the best choice. + /// First, it is closest two how + /// objects are most often represented. Second, it is compatible with results of many + /// PostgreSQL functions, particularly with age() and the results of subtracting one date, time or timestamp from + /// another. + /// + /// Note that the results of casting a to is + /// canonicalised. + /// + /// + /// An based on this one, but with months converted to multiples of ±30days and with any hours outside of the range [-23, 23] + /// converted into days. + public NpgsqlTimeSpan Canonicalize() + { + return new(0, Days + Months * DaysPerMonth + (int)(Ticks / TicksPerDay), Ticks % TicksPerDay); + } + + #endregion + + #region Casts + + /// + /// Implicit cast of a to an + /// + /// A + /// An eqivalent, canonical, . + public static implicit operator NpgsqlTimeSpan(TimeSpan timespan) => ToNpgsqlTimeSpan(timespan); + + /// + /// Casts a to an . + /// + public static NpgsqlTimeSpan ToNpgsqlTimeSpan(TimeSpan timespan) => new NpgsqlTimeSpan(timespan).Canonicalize(); + + /// + /// Explicit cast of an to a . + /// + /// A . + /// An equivalent . + public static explicit operator TimeSpan(NpgsqlTimeSpan interval) + => ToTimeSpan(interval); + + /// + /// Casts an to a . + /// + public static TimeSpan ToTimeSpan(in NpgsqlTimeSpan interval) + => interval.Months > 0 + ? throw new InvalidCastException("Cannot convert interval value with non-zero months to TimeSpan") + : new(interval.Ticks + interval.Days * TicksPerDay); + + #endregion + + #region Comparison + + /// + /// Returns true if another is exactly the same as this instance. + /// + /// An for comparison. + /// true if the two instances are exactly the same, + /// false otherwise. + public bool Equals(NpgsqlTimeSpan other) + => Ticks == other.Ticks && Days == other.Days && Months == other.Months; + + /// + /// Returns true if another object is an , that is exactly the same as + /// this instance + /// + /// An for comparison. + /// true if the argument is an and is exactly the same + /// as this one, false otherwise. + public override bool Equals(object? obj) => obj is NpgsqlTimeSpan span && Equals(span); + + /// + /// Compares two instances. + /// + /// The first . + /// The second . + /// 0 if the two are equal or equivalent. A value greater than zero if x is greater than y, + /// a value less than zero if x is less than y. + public static int Compare(NpgsqlTimeSpan x, NpgsqlTimeSpan y) => x.CompareTo(y); + + int IComparer.Compare(NpgsqlTimeSpan x, NpgsqlTimeSpan y) => x.CompareTo(y); + + int IComparer.Compare(object? x, object? y) + { + if (x == null) + return y == null ? 0 : 1; + if (y == null) + return -1; + try { + return ((IComparable)x).CompareTo(y); + } catch (Exception) { + throw new ArgumentException(); + } + } + + /// + /// A hash code suitable for uses with hashing algorithms. + /// + /// An signed integer. + public override int GetHashCode() => UnjustifyInterval().Ticks.GetHashCode(); + + /// + /// Compares this instance with another/ + /// + /// An to compare this with. + /// 0 if the instances are equal or equivalent. A value less than zero if + /// this instance is less than the argument. A value greater than zero if this instance + /// is greater than the instance. + public int CompareTo(NpgsqlTimeSpan other) + => UnjustifyInterval().Ticks.CompareTo(other.UnjustifyInterval().Ticks); + + /// + /// Compares this instance with another/ + /// + /// An object to compare this with. + /// 0 if the argument is an and the instances are equal or equivalent. + /// A value less than zero if the argument is an and + /// this instance is less than the argument. + /// A value greater than zero if the argument is an and this instance + /// is greater than the instance. + /// A value greater than zero if the argument is null. + /// The argument is not an . + public int CompareTo(object? other) + { + if (other == null) + return 1; + if (other is NpgsqlTimeSpan) + return CompareTo((NpgsqlTimeSpan)other); + throw new ArgumentException(nameof(other)); + } + + #endregion + + #region String Conversions + + /// + /// Parses a and returns a instance. + /// Designed to use the formats generally returned by PostgreSQL. + /// + /// The to parse. + /// An represented by the argument. + /// The string was null. + /// A value obtained from parsing the string exceeded the values allowed for the relevant component. + /// The string was not in a format that could be parsed to produce an . + public static NpgsqlTimeSpan Parse(string str) + { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + str = str.Replace('s', ' '); //Quick and easy way to catch plurals. + try { + var years = 0; + var months = 0; + var days = 0; + var hours = 0; + var minutes = 0; + var seconds = 0m; + var idx = str.IndexOf("year", StringComparison.Ordinal); + if (idx > 0) { + years = int.Parse(str.Substring(0, idx)); + str = SafeSubstring(str, idx + 5); + } + idx = str.IndexOf("mon", StringComparison.Ordinal); + if (idx > 0) { + months = int.Parse(str.Substring(0, idx)); + str = SafeSubstring(str, idx + 4); + } + idx = str.IndexOf("day", StringComparison.Ordinal); + if (idx > 0) { + days = int.Parse(str.Substring(0, idx)); + str = SafeSubstring(str, idx + 4).Trim(); + } + if (str.Length > 0) { + var isNegative = str[0] == '-'; + var parts = str.Split(':'); + switch (parts.Length) //One of those times that fall-through would actually be good. + { + case 1: + hours = int.Parse(parts[0]); + break; + case 2: + hours = int.Parse(parts[0]); + minutes = int.Parse(parts[1]); + break; + default: + hours = int.Parse(parts[0]); + minutes = int.Parse(parts[1]); + seconds = decimal.Parse(parts[2], System.Globalization.CultureInfo.InvariantCulture.NumberFormat); + break; + } + if (isNegative) { + minutes *= -1; + seconds *= -1; + } + } + var ticks = hours * TicksPerHour + minutes * TicksPerMinute + (long)(seconds * TicksPerSecond); + return new NpgsqlTimeSpan(years * MonthsPerYear + months, days, ticks); + } catch (OverflowException) { + throw; + } catch (Exception) { + throw new FormatException(); + } + } + + private static string SafeSubstring(string s, int startIndex) + { + if (startIndex >= s.Length) + return string.Empty; + else + return s.Substring(startIndex); + } + + /// + /// Attempt to parse a to produce an . + /// + /// The to parse. + /// (out) The produced, or if the parsing failed. + /// true if the parsing succeeded, false otherwise. + public static bool TryParse(string str, out NpgsqlTimeSpan result) + { + try { + result = Parse(str); + return true; + } catch (Exception) { + result = Zero; + return false; + } + } + + /// + /// Create a representation of the instance. + /// The format returned is of the form: + /// [M mon[s]] [d day[s]] [HH:mm:ss[.f[f[f[f[f[f[f[f[f]]]]]]]]]] + /// A zero is represented as 00:00:00 + /// + /// Ticks are 100ns, Postgress resolution is only to 1µs at most. Hence we lose 1 or more decimal + /// precision in storing values in the database. Despite this, this method will output that extra + /// digit of precision. It's forward-compatible with any future increases in resolution up to 100ns, + /// and also makes this ToString() more applicable to any other use-case. + /// + /// + /// The representation. + public override string ToString() + { + var sb = new StringBuilder(); + if (Months != 0) { + sb.Append(Months).Append(Math.Abs(Months) == 1 ? " mon " : " mons "); + } + if (Days != 0) { + if (Months < 0 && Days > 0) { + sb.Append('+'); + } + sb.Append(Days).Append(Math.Abs(Days) == 1 ? " day " : " days "); + } + if (Ticks != 0 || sb.Length == 0) { + if (Ticks < 0) { + sb.Append('-'); + } else if (Days < 0 || (Days == 0 && Months < 0)) { + sb.Append('+'); + } + // calculate total seconds and then subtract total whole minutes in seconds to get just the seconds and fractional part + var seconds = _ticks / (decimal)TicksPerSecond - (_ticks / TicksPerMinute) * 60; + sb.Append(Math.Abs(Hours).ToString("D2")).Append(':').Append(Math.Abs(Minutes).ToString("D2")).Append(':').Append(Math.Abs(seconds).ToString("0#.######", System.Globalization.CultureInfo.InvariantCulture.NumberFormat)); + + } + if (sb[sb.Length - 1] == ' ') { + sb.Remove(sb.Length - 1, 1); + } + return sb.ToString(); + } + + #endregion + + #region Common Operators + + /// + /// Adds two together. + /// + /// The first to add. + /// The second to add. + /// An whose values are the sum of the arguments. + public static NpgsqlTimeSpan operator +(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return x.Add(y); + } + + /// + /// Subtracts one from another. + /// + /// The to subtract the other from. + /// The to subtract from the other. + /// An whose values are the difference of the arguments + public static NpgsqlTimeSpan operator -(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return x.Subtract(y); + } + + /// + /// Returns true if two are exactly the same. + /// + /// The first to compare. + /// The second to compare. + /// true if the two arguments are exactly the same, false otherwise. + public static bool operator ==(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return x.Equals(y); + } + + /// + /// Returns false if two are exactly the same. + /// + /// The first to compare. + /// The second to compare. + /// false if the two arguments are exactly the same, true otherwise. + public static bool operator !=(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return !(x == y); + } + + /// + /// Compares two instances to see if the first is less than the second + /// + /// The first to compare. + /// The second to compare. + /// true if the first is less than second, false otherwise. + public static bool operator <(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return x.UnjustifyInterval().Ticks < y.UnjustifyInterval().Ticks; + } + + /// + /// Compares two instances to see if the first is less than or equivalent to the second + /// + /// The first to compare. + /// The second to compare. + /// true if the first is less than or equivalent to second, false otherwise. + public static bool operator <=(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return x.UnjustifyInterval().Ticks <= y.UnjustifyInterval().Ticks; + } + + /// + /// Compares two instances to see if the first is greater than the second + /// + /// The first to compare. + /// The second to compare. + /// true if the first is greater than second, false otherwise. + public static bool operator >(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return !(x <= y); + } + + /// + /// Compares two instances to see if the first is greater than or equivalent the second + /// + /// The first to compare. + /// The second to compare. + /// true if the first is greater than or equivalent to the second, false otherwise. + public static bool operator >=(NpgsqlTimeSpan x, NpgsqlTimeSpan y) + { + return !(x < y); + } + + /// + /// Returns the instance. + /// + public static NpgsqlTimeSpan operator +(NpgsqlTimeSpan x) => Plus(x); + + /// + /// Returns the instance. + /// + public static NpgsqlTimeSpan Plus(in NpgsqlTimeSpan x) => x; + + /// + /// Negates an instance. + /// + /// An . + /// The negation of the argument. + public static NpgsqlTimeSpan operator -(NpgsqlTimeSpan x) => x.Negate(); + + #endregion +} diff --git a/EF6.PG/NpgsqlTypes/NpgsqlTimeSpan.cs b/EF6.PG/NpgsqlTypes/NpgsqlTimeSpan.cs new file mode 100644 index 0000000..6bc0e99 --- /dev/null +++ b/EF6.PG/NpgsqlTypes/NpgsqlTimeSpan.cs @@ -0,0 +1,510 @@ +#nullable enable +using System; +using System.Collections; +using System.Collections.Generic; +using Npgsql.Util; + +#pragma warning disable 1591 + +// ReSharper disable once CheckNamespace +namespace NpgsqlTypes; + +/// +/// A struct similar to .NET DateTime but capable of storing PostgreSQL's timestamp and timestamptz types. +/// DateTime is capable of storing values from year 1 to 9999 at 100-nanosecond precision, +/// while PostgreSQL's timestamps store values from 4713BC to 5874897AD with 1-microsecond precision. +/// +//[Obsolete( +// "For values outside the range of DateTime, consider using NodaTime (range -9998 to 9999), or read the value as a 'long'. " + +// "See https://www.npgsql.org/doc/types/datetime.html for more information.")] +[Serializable] +public readonly struct NpgsqlDateTime : IEquatable, IComparable, IComparable, + IComparer, IComparer +{ + #region Fields + + readonly NpgsqlDate _date; + readonly TimeSpan _time; + readonly InternalType _type; + + #endregion + + #region Constants + + public static readonly NpgsqlDateTime Epoch = new(NpgsqlDate.Epoch); + public static readonly NpgsqlDateTime Era = new(NpgsqlDate.Era); + + public static readonly NpgsqlDateTime Infinity = + new(InternalType.Infinity, NpgsqlDate.Era, TimeSpan.Zero); + + public static readonly NpgsqlDateTime NegativeInfinity = + new(InternalType.NegativeInfinity, NpgsqlDate.Era, TimeSpan.Zero); + + // 9999-12-31 + const int MaxDateTimeDay = 3652058; + + #endregion + + #region Constructors + + NpgsqlDateTime(InternalType type, NpgsqlDate date, TimeSpan time) + { + if (!date.IsFinite && type != InternalType.Infinity && type != InternalType.NegativeInfinity) + throw new ArgumentException("Can't construct an NpgsqlDateTime with a non-finite date, use Infinity and NegativeInfinity instead", nameof(date)); + + _type = type; + _date = date; + _time = time; + } + + public NpgsqlDateTime(NpgsqlDate date, TimeSpan time, DateTimeKind kind = DateTimeKind.Unspecified) + : this(KindToInternalType(kind), date, time) {} + + public NpgsqlDateTime(NpgsqlDate date) + : this(date, TimeSpan.Zero) {} + + public NpgsqlDateTime(int year, int month, int day, int hours, int minutes, int seconds, DateTimeKind kind=DateTimeKind.Unspecified) + : this(new NpgsqlDate(year, month, day), new TimeSpan(0, hours, minutes, seconds), kind) {} + + public NpgsqlDateTime(int year, int month, int day, int hours, int minutes, int seconds, int milliseconds, DateTimeKind kind = DateTimeKind.Unspecified) + : this(new NpgsqlDate(year, month, day), new TimeSpan(0, hours, minutes, seconds, milliseconds), kind) { } + + public NpgsqlDateTime(DateTime dateTime) + : this(new NpgsqlDate(dateTime.Date), dateTime.TimeOfDay, dateTime.Kind) {} + + public NpgsqlDateTime(long ticks, DateTimeKind kind) + : this(new DateTime(ticks, kind)) { } + + public NpgsqlDateTime(long ticks) + : this(new DateTime(ticks, DateTimeKind.Unspecified)) { } + + #endregion + + #region Public Properties + + public NpgsqlDate Date => _date; + public TimeSpan Time => _time; + public int DayOfYear => _date.DayOfYear; + public int Year => _date.Year; + public int Month => _date.Month; + public int Day => _date.Day; + public DayOfWeek DayOfWeek => _date.DayOfWeek; + public bool IsLeapYear => _date.IsLeapYear; + + public long Ticks => _date.DaysSinceEra * NpgsqlTimeSpan.TicksPerDay + _time.Ticks; + public int Millisecond => _time.Milliseconds; + public int Second => _time.Seconds; + public int Minute => _time.Minutes; + public int Hour => _time.Hours; + public bool IsInfinity => _type == InternalType.Infinity; + public bool IsNegativeInfinity => _type == InternalType.NegativeInfinity; + + public bool IsFinite + => _type switch + { + InternalType.FiniteUnspecified => true, + InternalType.FiniteUtc => true, + InternalType.FiniteLocal => true, + InternalType.Infinity => false, + InternalType.NegativeInfinity => false, + _ => throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDateTime)}.{nameof(InternalType)}. Please file a bug.") + }; + + public DateTimeKind Kind + => _type switch + { + InternalType.FiniteUtc => DateTimeKind.Utc, + InternalType.FiniteLocal => DateTimeKind.Local, + InternalType.FiniteUnspecified => DateTimeKind.Unspecified, + InternalType.Infinity => DateTimeKind.Unspecified, + InternalType.NegativeInfinity => DateTimeKind.Unspecified, + _ => throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(DateTimeKind)}. Please file a bug.") + }; + + /// + /// Cast of an to a . + /// + /// An equivalent . + public DateTime ToDateTime() + { + if (!IsFinite) + throw new InvalidCastException("Can't convert infinite timestamp values to DateTime"); + + if (_date.DaysSinceEra < 0 || _date.DaysSinceEra > MaxDateTimeDay) + throw new InvalidCastException("Out of the range of DateTime (year must be between 1 and 9999)"); + + return new DateTime(Ticks, Kind); + } + + /// + /// Converts the value of the current object to Coordinated Universal Time (UTC). + /// + /// + /// See the MSDN documentation for DateTime.ToUniversalTime(). + /// Note: this method only takes into account the time zone's base offset, and does + /// not respect daylight savings. See https://github.com/npgsql/npgsql/pull/684 for more + /// details. + /// + public NpgsqlDateTime ToUniversalTime() + { + switch (_type) + { + case InternalType.FiniteUnspecified: + // Treat as Local + case InternalType.FiniteLocal: + if (_date.DaysSinceEra >= 1 && _date.DaysSinceEra <= MaxDateTimeDay - 1) + { + // Day between 0001-01-02 and 9999-12-30, so we can use DateTime and it will always succeed + return new NpgsqlDateTime(Subtract(TimeZoneInfo.Local.GetUtcOffset(new DateTime(ToDateTime().Ticks, DateTimeKind.Local))).Ticks, DateTimeKind.Utc); + } + // Else there are no DST rules available in the system for outside the DateTime range, so just use the base offset + var timeTicks = _time.Ticks - TimeZoneInfo.Local.BaseUtcOffset.Ticks; + var date = _date; + if (timeTicks < 0) + { + timeTicks += NpgsqlTimeSpan.TicksPerDay; + date = date.AddDays(-1); + } + else if (timeTicks > NpgsqlTimeSpan.TicksPerDay) + { + timeTicks -= NpgsqlTimeSpan.TicksPerDay; + date = date.AddDays(1); + } + return new NpgsqlDateTime(date, TimeSpan.FromTicks(timeTicks), DateTimeKind.Utc); + case InternalType.FiniteUtc: + case InternalType.Infinity: + case InternalType.NegativeInfinity: + return this; + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDateTime)}.{nameof(InternalType)}. Please file a bug."); + } + } + + /// + /// Converts the value of the current object to local time. + /// + /// + /// See the MSDN documentation for DateTime.ToLocalTime(). + /// Note: this method only takes into account the time zone's base offset, and does + /// not respect daylight savings. See https://github.com/npgsql/npgsql/pull/684 for more + /// details. + /// + public NpgsqlDateTime ToLocalTime() + { + switch (_type) { + case InternalType.FiniteUnspecified: + // Treat as UTC + case InternalType.FiniteUtc: + if (_date.DaysSinceEra >= 1 && _date.DaysSinceEra <= MaxDateTimeDay - 1) + { + // Day between 0001-01-02 and 9999-12-30, so we can use DateTime and it will always succeed + return new NpgsqlDateTime(TimeZoneInfo.ConvertTime(new DateTime(ToDateTime().Ticks, DateTimeKind.Utc), TimeZoneInfo.Local)); + } + // Else there are no DST rules available in the system for outside the DateTime range, so just use the base offset + var timeTicks = _time.Ticks + TimeZoneInfo.Local.BaseUtcOffset.Ticks; + var date = _date; + if (timeTicks < 0) + { + timeTicks += NpgsqlTimeSpan.TicksPerDay; + date = date.AddDays(-1); + } + else if (timeTicks > NpgsqlTimeSpan.TicksPerDay) + { + timeTicks -= NpgsqlTimeSpan.TicksPerDay; + date = date.AddDays(1); + } + return new NpgsqlDateTime(date, TimeSpan.FromTicks(timeTicks), DateTimeKind.Local); + case InternalType.FiniteLocal: + case InternalType.Infinity: + case InternalType.NegativeInfinity: + return this; + default: + throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {_type} of enum {nameof(NpgsqlDateTime)}.{nameof(InternalType)}. Please file a bug."); + } + } + + public static NpgsqlDateTime Now => new(DateTime.Now); + + #endregion + + #region String Conversions + + public override string ToString() + => _type switch + { + InternalType.Infinity => "infinity", + InternalType.NegativeInfinity => "-infinity", + _ => $"{_date} {_time}" + }; + + public static NpgsqlDateTime Parse(string str) + { + if (str == null) { + throw new NullReferenceException(); + } + switch (str = str.Trim().ToLowerInvariant()) { + case "infinity": + return Infinity; + case "-infinity": + return NegativeInfinity; + default: + try { + var idxSpace = str.IndexOf(' '); + var datePart = str.Substring(0, idxSpace); + if (str.Contains("bc")) { + datePart += " BC"; + } + var idxSecond = str.IndexOf(' ', idxSpace + 1); + if (idxSecond == -1) { + idxSecond = str.Length; + } + var timePart = str.Substring(idxSpace + 1, idxSecond - idxSpace - 1); + return new NpgsqlDateTime(NpgsqlDate.Parse(datePart), TimeSpan.Parse(timePart)); + } catch (OverflowException) { + throw; + } catch { + throw new FormatException(); + } + } + } + + #endregion + + #region Comparisons + + public bool Equals(NpgsqlDateTime other) + => _type switch + { + InternalType.Infinity => other._type == InternalType.Infinity, + InternalType.NegativeInfinity => other._type == InternalType.NegativeInfinity, + _ => other._type == _type && _date.Equals(other._date) && _time.Equals(other._time) + }; + + public override bool Equals(object? obj) + => obj is NpgsqlDateTime time && Equals(time); + + public override int GetHashCode() + => _type switch + { + InternalType.Infinity => int.MaxValue, + InternalType.NegativeInfinity => int.MinValue, + _ => _date.GetHashCode() ^ RotateShift(_time.GetHashCode(), 16) + }; + + static int RotateShift(int val, int shift) + { + return (val << shift) | (val >> (sizeof (int) - shift)); + } + + public int CompareTo(NpgsqlDateTime other) + { + switch (_type) { + case InternalType.Infinity: + return other._type == InternalType.Infinity ? 0 : 1; + case InternalType.NegativeInfinity: + return other._type == InternalType.NegativeInfinity ? 0 : -1; + default: + switch (other._type) { + case InternalType.Infinity: + return -1; + case InternalType.NegativeInfinity: + return 1; + default: + var cmp = _date.CompareTo(other._date); + return cmp == 0 ? _time.CompareTo(other._time) : cmp; + } + } + } + + public int CompareTo(object? o) + => o == null + ? 1 + : o is NpgsqlDateTime npgsqlDateTime + ? CompareTo(npgsqlDateTime) + : throw new ArgumentException(); + + public int Compare(NpgsqlDateTime x, NpgsqlDateTime y) => x.CompareTo(y); + + public int Compare(object? x, object? y) + { + if (x == null) + return y == null ? 0 : -1; + if (y == null) + return 1; + if (!(x is IComparable) || !(y is IComparable)) + throw new ArgumentException(); + return ((IComparable)x).CompareTo(y); + } + + #endregion + + #region Arithmetic + + /// + /// Returns a new that adds the value of the specified to the value of this instance. + /// + /// An NpgsqlTimeSpan interval. + /// An object whose value is the sum of the date and time represented by this instance and the time interval represented by value. + public NpgsqlDateTime Add(in NpgsqlTimeSpan value) => AddTicks(value.UnjustifyInterval().TotalTicks); + + /// + /// Returns a new that adds the value of the specified TimeSpan to the value of this instance. + /// + /// A positive or negative time interval. + /// An object whose value is the sum of the date and time represented by this instance and the time interval represented by value. + public NpgsqlDateTime Add(TimeSpan value) { return AddTicks(value.Ticks); } + + /// + /// Returns a new that adds the specified number of years to the value of this instance. + /// + /// A number of years. The value parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of years represented by value. + public NpgsqlDateTime AddYears(int value) + => _type switch + { + InternalType.Infinity => this, + InternalType.NegativeInfinity => this, + _ => new NpgsqlDateTime(_type, _date.AddYears(value), _time) + }; + + /// + /// Returns a new that adds the specified number of months to the value of this instance. + /// + /// A number of months. The months parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and months. + public NpgsqlDateTime AddMonths(int value) + => _type switch + { + InternalType.Infinity => this, + InternalType.NegativeInfinity => this, + _ => new NpgsqlDateTime(_type, _date.AddMonths(value), _time) + }; + + /// + /// Returns a new that adds the specified number of days to the value of this instance. + /// + /// A number of whole and fractional days. The value parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of days represented by value. + public NpgsqlDateTime AddDays(double value) => Add(TimeSpan.FromDays(value)); + + /// + /// Returns a new that adds the specified number of hours to the value of this instance. + /// + /// A number of whole and fractional hours. The value parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of hours represented by value. + public NpgsqlDateTime AddHours(double value) => Add(TimeSpan.FromHours(value)); + + /// + /// Returns a new that adds the specified number of minutes to the value of this instance. + /// + /// A number of whole and fractional minutes. The value parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of minutes represented by value. + public NpgsqlDateTime AddMinutes(double value) => Add(TimeSpan.FromMinutes(value)); + + /// + /// Returns a new that adds the specified number of minutes to the value of this instance. + /// + /// A number of whole and fractional minutes. The value parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of minutes represented by value. + public NpgsqlDateTime AddSeconds(double value) => Add(TimeSpan.FromSeconds(value)); + + /// + /// Returns a new that adds the specified number of milliseconds to the value of this instance. + /// + /// A number of whole and fractional milliseconds. The value parameter can be negative or positive. Note that this value is rounded to the nearest integer. + /// An object whose value is the sum of the date and time represented by this instance and the number of milliseconds represented by value. + public NpgsqlDateTime AddMilliseconds(double value) => Add(TimeSpan.FromMilliseconds(value)); + + /// + /// Returns a new that adds the specified number of ticks to the value of this instance. + /// + /// A number of 100-nanosecond ticks. The value parameter can be positive or negative. + /// An object whose value is the sum of the date and time represented by this instance and the time represented by value. + public NpgsqlDateTime AddTicks(long value) + => _type switch + { + InternalType.Infinity => this, + InternalType.NegativeInfinity => this, + _ => new NpgsqlDateTime(Ticks + value, Kind), + }; + + public NpgsqlDateTime Subtract(in NpgsqlTimeSpan interval) => Add(-interval); + + public NpgsqlTimeSpan Subtract(NpgsqlDateTime timestamp) + { + switch (_type) { + case InternalType.Infinity: + case InternalType.NegativeInfinity: + throw new InvalidOperationException("You cannot subtract infinity timestamps"); + } + switch (timestamp._type) { + case InternalType.Infinity: + case InternalType.NegativeInfinity: + throw new InvalidOperationException("You cannot subtract infinity timestamps"); + } + return new NpgsqlTimeSpan(0, _date.DaysSinceEra - timestamp._date.DaysSinceEra, _time.Ticks - timestamp._time.Ticks); + } + + #endregion + + #region Operators + + public static NpgsqlDateTime operator +(NpgsqlDateTime timestamp, NpgsqlTimeSpan interval) + => timestamp.Add(interval); + + public static NpgsqlDateTime operator +(NpgsqlTimeSpan interval, NpgsqlDateTime timestamp) + => timestamp.Add(interval); + + public static NpgsqlDateTime operator -(NpgsqlDateTime timestamp, NpgsqlTimeSpan interval) + => timestamp.Subtract(interval); + + public static NpgsqlTimeSpan operator -(NpgsqlDateTime x, NpgsqlDateTime y) => x.Subtract(y); + public static bool operator ==(NpgsqlDateTime x, NpgsqlDateTime y) => x.Equals(y); + public static bool operator !=(NpgsqlDateTime x, NpgsqlDateTime y) => !(x == y); + public static bool operator <(NpgsqlDateTime x, NpgsqlDateTime y) => x.CompareTo(y) < 0; + public static bool operator >(NpgsqlDateTime x, NpgsqlDateTime y) => x.CompareTo(y) > 0; + public static bool operator <=(NpgsqlDateTime x, NpgsqlDateTime y) => x.CompareTo(y) <= 0; + public static bool operator >=(NpgsqlDateTime x, NpgsqlDateTime y) => x.CompareTo(y) >= 0; + + #endregion + + #region Casts + + /// + /// Implicit cast of a to an + /// + /// A + /// An equivalent . + public static implicit operator NpgsqlDateTime(DateTime dateTime) => ToNpgsqlDateTime(dateTime); + public static NpgsqlDateTime ToNpgsqlDateTime(DateTime dateTime) => new(dateTime); + + /// + /// Explicit cast of an to a . + /// + /// An . + /// An equivalent . + public static explicit operator DateTime(NpgsqlDateTime npgsqlDateTime) + => npgsqlDateTime.ToDateTime(); + + #endregion + + public NpgsqlDateTime Normalize() => Add(NpgsqlTimeSpan.Zero); + + static InternalType KindToInternalType(DateTimeKind kind) + => kind switch + { + DateTimeKind.Unspecified => InternalType.FiniteUnspecified, + DateTimeKind.Utc => InternalType.FiniteUtc, + DateTimeKind.Local => InternalType.FiniteLocal, + _ => throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {kind} of enum {nameof(NpgsqlDateTime)}.{nameof(InternalType)}. Please file a bug.") + }; + + enum InternalType + { + FiniteUnspecified, + FiniteUtc, + FiniteLocal, + Infinity, + NegativeInfinity + } +} diff --git a/EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml b/EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml index 527cc10..2e4d5bf 100644 --- a/EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml +++ b/EF6.PG/Properties/PublishProfiles/FolderProfile.pubxml @@ -6,8 +6,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Debug Any CPU - bin\Release\net45\publish\ + bin\Release\net6.0\publish\ FileSystem - net45 + net6.0 \ No newline at end of file diff --git a/EF6.PG/Spatial/PostgisDataReader.cs b/EF6.PG/Spatial/PostgisDataReader.cs deleted file mode 100644 index 60e901d..0000000 --- a/EF6.PG/Spatial/PostgisDataReader.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Data; -using System.Data.Entity.Spatial; - -namespace Npgsql.Spatial -{ - /// - /// A postgis geometry service API. - /// - public class PostgisDataReader : DbSpatialDataReader, IDataReader - { - PostgisServices _svcs; - NpgsqlDataReader _rdr; - - /// - /// Creates a new instance of postgis data reader using a specific instance of PostgisService. - /// - /// The service provider that DbGeometry instances will use. - /// The underlying data reader. - public PostgisDataReader(PostgisServices svcs, NpgsqlDataReader rdr) - { - _svcs = svcs; - _rdr = rdr; - } - - /// - /// Creates a new instance of postgis data reader. - /// - /// The underlying data reader. - public PostgisDataReader(NpgsqlDataReader rdr) : this(new PostgisServices(),rdr) {} - - /// - /// Get the DbGeography value of a column, given its zero-based ordinal. - /// - public override DbGeography GetGeography(int ordinal) - => throw new NotImplementedException(); - - /// - /// Get the DbGeometry value of a column, given its zero-based ordinal. - /// - public override DbGeometry GetGeometry(int ordinal) - => throw new NotImplementedException(); - - /// - /// Get the value indicating wether a column is a Geometry value, given its zero-based ordinal. - /// - public override bool IsGeometryColumn(int ordinal) - => throw new NotImplementedException(); - - /// - /// Get the value indicating wether a column is a Geography value, given its zero-based ordinal. - /// - public override bool IsGeographyColumn(int ordinal) - => throw new NotImplementedException(); - - /// - /// Gets a value indicating the depth of nesting of the current row. Always Zero. - /// - public int Depth => _rdr.Depth; - - /// - /// Gets a value indicating wether the data reader is closed. - /// - public bool IsClosed => _rdr.IsClosed; - - /// - /// Gets the number of row affected by the SQL statement. - /// - public int RecordsAffected => _rdr.RecordsAffected; - - - /// - /// Gets the number of columns in the current row. - /// - public int FieldCount => _rdr.FieldCount; - - /// - /// Gets the value of the specified column name of the current row. - /// - public object this[string name] => _rdr[name]; - - /// - /// Gets the value of the specified column index of the current row. - /// - public object this[int i] => _rdr[i]; - - /// - /// Close the underlying datareader object. - /// - public void Close() => _rdr.Close(); - - /// - /// Returns a DataTable which contains metadata about the current row. - /// - public DataTable GetSchemaTable() => _rdr.GetSchemaTable(); - - /// - /// Advances the reader to the next result when reading data from a batch of statements. - /// - /// - public bool NextResult() => _rdr.NextResult(); - - /// - /// Advances the reader to the next record in a result set. - /// - /// - public bool Read() => _rdr.Read(); - - /// - /// Frees the resources hold by the data reader. - /// - public void Dispose() => _rdr.Dispose(); - - /// - /// Gets the name of a column, given a zero based ordinal. - /// - public string GetName(int i) => _rdr.GetName(i); - - /// - /// Gets the data type name of a column, given a zero based ordinal. - /// - public string GetDataTypeName(int i) => _rdr.GetDataTypeName(i); - - /// - /// Gets the System.Type of a column, given a zero based ordinal. - /// - public Type GetFieldType(int i) => _rdr.GetFieldType(i); - - /// - /// Gets the value of a column, given a zero based ordinal. - /// - public object GetValue(int i) => _rdr.GetValue(i); - - /// - /// Populates an array of objects with the values of the current row. - /// - public int GetValues(object[] values) => _rdr.GetValues(values); - - /// - /// Gets the column ordinal given the column name. - /// - public int GetOrdinal(string name) => _rdr.GetOrdinal(name); - - /// - /// Get the value of a column as a boolean, given its zero-based ordinal. - /// - public bool GetBoolean(int i) => _rdr.GetBoolean(i); - - /// - /// Get the value of a column as a byte, given its zero-based ordinal. - /// - public byte GetByte(int i) => _rdr.GetByte(i); - - /// - /// Populates a byte array with the value of a column, given its zero-based ordinal. - /// - public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - => _rdr.GetBytes(i, fieldOffset, buffer, bufferoffset, length); - - /// - /// Get the value of a column as a char, given its zero-based ordinal. - /// - public char GetChar(int i) => _rdr.GetChar(i); - - public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) - => _rdr.GetChars(i, fieldoffset, buffer, bufferoffset, length); - - /// - /// Get the value of a column as a GUID, given its zero-based ordinal. - /// - public Guid GetGuid(int i) => _rdr.GetGuid(i); - - /// - /// Get the value of a column as an int16, given its zero-based ordinal. - /// - public short GetInt16(int i) => _rdr.GetInt16(i); - - /// - /// Get the value of a column as an int32, given its zero-based ordinal. - /// - public int GetInt32(int i) => _rdr.GetInt32(i); - - /// - /// Get the value of a column as an int64, given its zero-based ordinal. - /// - public long GetInt64(int i) => _rdr.GetInt64(i); - - /// - /// Get the value of a column as a float, given its zero-based ordinal. - /// - public float GetFloat(int i) => _rdr.GetFloat(i); - - /// - /// Get the value of a column as a double, given its zero-based ordinal. - /// - public double GetDouble(int i) => _rdr.GetDouble(i); - - /// - /// Get the value of a column as a string, given its zero-based ordinal. - /// - public string GetString(int i) - { - return _rdr.GetString(i); - } - - /// - /// Get the value of a column as a decimal, given its zero-based ordinal. - /// - public decimal GetDecimal(int i) - { - return _rdr.GetDecimal(i); - } - - /// - /// Get the value of a column as a datetime, given its zero-based ordinal. - /// - public DateTime GetDateTime(int i) - { - return _rdr.GetDateTime(i); - } - - /// - /// Returns a DbDataReader of a column, given its zero-based ordinal. - /// - public IDataReader GetData(int i) - { - return _rdr.GetData(i); - } - - /// - /// Get the value indicating wether the column contains non-existent or missing value, given its zero-based ordinal. - /// - public bool IsDBNull(int i) - { - return _rdr.IsDBNull(i); - } - } -} diff --git a/EF6.PG/Spatial/PostgisServices.cs b/EF6.PG/Spatial/PostgisServices.cs deleted file mode 100644 index 227625c..0000000 --- a/EF6.PG/Spatial/PostgisServices.cs +++ /dev/null @@ -1,1283 +0,0 @@ -using System; -using System.Data.Entity.Spatial; -using NpgsqlTypes; - -namespace Npgsql.Spatial -{ - /// - /// A class exposing spatial services. - /// - public class PostgisServices : DbSpatialServices - { - /// - /// Returns the well known binary value of the geometry input. - /// - public override byte[] AsBinary(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_AsBinary(:p1)"; - return (byte[])cmd.ExecuteScalar(); - } - } - - /// - /// Returns the well known binary value of the geography input. - /// - public override byte[] AsBinary(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the geographical markup language representation of the geometry input. - /// - public override string AsGml(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_AsGml(:p1)"; - return (string)cmd.ExecuteScalar(); - } - } - - /// - /// Returns the geographical markup language representation of the geography input. - /// - public override string AsGml(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the well known text representation of the geometry input. - /// - public override string AsText(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_AsText(:p1)"; - return (string)cmd.ExecuteScalar(); - } - } - - /// - /// Returns the well known text representation of the geography input. - /// - public override string AsText(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns a geometry that represents all points whose distance from this Geometry is less than or equal to distance. - /// - public override DbGeometry Buffer(DbGeometry geometryValue, double distance) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Double, distance); - cmd.CommandText = "SELECT ST_Buffer(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns a geometry that represents all points whose distance from this Geometry is less than or equal to distance. - /// Calculations are in the Spatial Reference System of this Geometry. Uses a planar transform wrapper. - /// - public override DbGeography Buffer(DbGeography geographyValue, double distance) - => throw new NotImplementedException(); - - /// - /// Returns true if and only if no points of B lie in the exterior of A, and at least one point of the interior of B lies in the interior of A - /// - public override bool Contains(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Contains(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - public override object CreateProviderValue(DbGeometryWellKnownValue wellKnownValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, wellKnownValue.WellKnownBinary); - cmd.CommandText = "SELECT ST_GeomFromWkb(:p1)"; - return cmd.ExecuteScalar(); - } - } - - /// - public override object CreateProviderValue(DbGeographyWellKnownValue wellKnownValue) - => throw new NotImplementedException(); - - /// - public override DbGeometryWellKnownValue CreateWellKnownValue(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_AsText(:p1)"; - var d = new DbGeometryWellKnownValue(); - d.WellKnownText = (string)cmd.ExecuteScalar(); - cmd.CommandText = "SELECT ST_AsBinary(:p1)"; - d.WellKnownBinary = (byte[])cmd.ExecuteScalar(); - cmd.CommandText = "SELECT ST_SRID(:p1)"; - d.CoordinateSystemId = (int)cmd.ExecuteScalar(); - return d; - } - } - - /// - public override DbGeographyWellKnownValue CreateWellKnownValue(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns TRUE if the supplied geometries have some, but not all, interior points in commo - /// - public override bool Crosses(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Crosses(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Returns a geometry that represents that part of geometry A that does not intersect with geometry B. - /// - public override DbGeometry Difference(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Difference(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns a geometry that represents that part of geometry A that does not intersect with geometry B. - /// - public override DbGeography Difference(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Returns TRUE if the Geometries do not "spatially intersect" - if they do not share any space together. - /// - public override bool Disjoint(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Disjoint(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Returns TRUE if the Geometries do not "spatially intersect" - if they do not share any space together. - /// - public override bool Disjoint(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Returns the 2-dimensional cartesian minimum distance (based on spatial ref) between two geometries in projected units. - /// - public override double Distance(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Distance(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetDouble(0); - } - } - } - - /// - /// Returns the 2-dimensional cartesian minimum distance (based on spatial ref) between two geometries in projected units. - /// - public override double Distance(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Given a geometry collection, returns the index-nth geometry. - /// - public override DbGeometry ElementAt(DbGeometry geometryValue, int index) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, index); - cmd.CommandText = "SELECT ST_GeometryN(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Given a geography collection, returns the index-nth geography. - /// - public override DbGeography ElementAt(DbGeography geographyValue, int index) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyCollectionFromBinary(byte[] geographyCollectionWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyCollectionFromText(string geographyCollectionWellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyFromBinary(byte[] wellKnownBinary) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyFromBinary(byte[] wellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyFromGml(string geographyMarkup) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyFromGml(string geographyMarkup, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyFromProviderValue(object providerValue) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyFromText(string wellKnownText) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyFromText(string wellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyLineFromBinary(byte[] lineWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyLineFromText(string lineWellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyMultiLineFromBinary(byte[] multiLineWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyMultiLineFromText(string multiLineWellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyMultiPointFromBinary(byte[] multiPointWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyMultiPointFromText(string multiPointWellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyMultiPolygonFromBinary(byte[] multiPolygonWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyMultiPolygonFromText(string multiPolygonWellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyPointFromBinary(byte[] pointWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyPointFromText(string pointWellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyPolygonFromBinary(byte[] polygonWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - public override DbGeography GeographyPolygonFromText(string polygonWellKnownText, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - /// Get the geometry collection from a well know binary representation. - /// - public override DbGeometry GeometryCollectionFromBinary(byte[] geometryCollectionWellKnownBinary, int coordinateSystemId) - => throw new NotImplementedException(); - - /// - /// Get the geometry collection from a well know binary representation. - /// - public override DbGeometry GeometryCollectionFromText(string geometryCollectionWellKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, geometryCollectionWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_GeomCollFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the geometry from its well known binary representation - /// - public override DbGeometry GeometryFromBinary(byte[] wellKnownBinary) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, wellKnownBinary); - cmd.CommandText = "SELECT ST_GeomFromWKB(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the geometry from its well known binary representation - /// - public override DbGeometry GeometryFromBinary(byte[] wellKnownBinary, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, wellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_GeomFromWKB(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the geometry from a geometic markup language representation. - /// - public override DbGeometry GeometryFromGml(string geometryMarkup) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, geometryMarkup); - cmd.CommandText = "SELECT ST_GeomFromGML(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the geometry from a geometic markup language representation. - /// - public override DbGeometry GeometryFromGml(string geometryMarkup, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, geometryMarkup); - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_GeomFromGML(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Wrap a npgsql geometry in a DbGeometry structure. - /// - public override DbGeometry GeometryFromProviderValue(object providerValue) - => CreateGeometry(this, providerValue); - - /// - /// Get the geometry from a well known text value. - /// - public override DbGeometry GeometryFromText(string wellKnownText) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, wellKnownText); - cmd.CommandText = "SELECT ST_GeomFromText(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the geometry from a well known text value. - /// - public override DbGeometry GeometryFromText(string wellKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, wellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_GeomFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a line from its well known binary value. - /// - public override DbGeometry GeometryLineFromBinary(byte[] lineWellKnownBinary, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, lineWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_LineFromWKB(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a line from its well known text value. - /// - public override DbGeometry GeometryLineFromText(string lineWellKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, lineWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_LineFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a multiline from its well known binary value. - /// - public override DbGeometry GeometryMultiLineFromBinary(byte[] multiLineWellKnownBinary, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, multiLineWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_MLineFromWKB(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a multiline from a well known text value. - /// - public override DbGeometry GeometryMultiLineFromText(string multiLineWellKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, multiLineWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_MLineFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a multipoint from its well known binaryrepresentation. - /// - public override DbGeometry GeometryMultiPointFromBinary(byte[] multiPointWellKnownBinary, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, multiPointWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_MPointFromWKB(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a multipoint from its well known text representation. - /// - public override DbGeometry GeometryMultiPointFromText(string multiPointWellKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, multiPointWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_MPointFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a multipolygon from its well known binary value. - /// - public override DbGeometry GeometryMultiPolygonFromBinary(byte[] multiPolygonWellKnownBinary, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, multiPolygonWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_MPolyFromWKB(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a multipolygon from its well known text value. - /// - public override DbGeometry GeometryMultiPolygonFromText(string multiPolygonKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, multiPolygonKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_MPolyFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a point from its well known binary value. - /// - public override DbGeometry GeometryPointFromBinary(byte[] pointWellKnownBinary, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, pointWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT st_GeomFromWKB(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a point from its well known text value. - /// - /// - /// - /// - public override DbGeometry GeometryPointFromText(string pointWellKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, pointWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_PointFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a polygon from its well known binary value. - /// - public override DbGeometry GeometryPolygonFromBinary(byte[] polygonWellKnownBinary, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Bytea, polygonWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_GeomFromWKB(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get a polygon from its well known text value. - /// - public override DbGeometry GeometryPolygonFromText(string polygonWellKnownText, int coordinateSystemId) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Text, polygonWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = "SELECT ST_GeometryFromText(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns the area of the surface if it is a polygon or multi-polygon. - /// - public override double? GetArea(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_Area(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : rdr.GetDouble(0); - } - } - } - - /// - /// Returns the area of the surface if it is a polygon or multi-polygon. - /// - public override double? GetArea(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the closure of the combinatorial boundary of the geometry. - /// - public override DbGeometry GetBoundary(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_Boundary(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns the centroid of the geometry. - /// - public override DbGeometry GetCentroid(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_Centroid(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the convex hull of the geometry. - /// - public override DbGeometry GetConvexHull(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_ConvexHull(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the SRID of the geometry. - /// - public override int GetCoordinateSystemId(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_SRID(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetInt32(0); - } - } - } - - /// - /// Get the SRID of the geography. - /// - public override int GetCoordinateSystemId(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Get the geometry dimension. - /// - public override int GetDimension(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_Dimension(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetInt32(0); - } - } - } - - /// - /// Get the geograpy dimension. - /// - public override int GetDimension(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Get the element count of the geometry collection. - /// - public override int? GetElementCount(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_NumGeometries(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new int() : rdr.GetInt32(0); - } - } - } - - /// - /// Get the element count of the geometry collection. - /// - public override int? GetElementCount(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the elevation of the geometry - /// - public override double? GetElevation(DbGeometry geometryValue) - => throw new NotImplementedException(); - - /// - /// Returns the elevation of the geography. - /// - public override double? GetElevation(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Get the endpoint of the geometry. - /// - public override DbGeometry GetEndPoint(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_EndPoint(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the endpoint of the geography. - /// - public override DbGeography GetEndPoint(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Get the envelope of the geometry. - /// - public override DbGeometry GetEnvelope(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_Envelope(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the exterior ring of the geometry. - /// - public override DbGeometry GetExteriorRing(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_ExteriorRing(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Get the ring count of the geometry. - /// - public override int? GetInteriorRingCount(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_NumInteriorRing(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetInt32(0); - } - } - } - - /// - /// Check if the geometry is closed. - /// - public override bool? GetIsClosed(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_IsClosed(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Check if the geography is closed; - /// - public override bool? GetIsClosed(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Chekc if the geometry is empty. - /// - public override bool GetIsEmpty(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_IsEmpty(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Check if the geography is empty. - /// - public override bool GetIsEmpty(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Check if the geometry is a linestring, simple and closed. - /// - public override bool? GetIsRing(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_IsRing(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Check if the geometry is simple. - /// - public override bool GetIsSimple(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_IsSimple(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Check if the geometry is valid. - /// - public override bool GetIsValid(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_IsValid(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Returns the latitude of the geography. - /// - public override double? GetLatitude(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the length of the geometry. - /// - public override double? GetLength(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_Length(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : rdr.GetDouble(0); - } - } - } - - /// - /// Returns the length of the geography. - /// - public override double? GetLength(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the longitutde of the geography. - /// - public override double? GetLongitude(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - public override double? GetMeasure(DbGeometry geometryValue) - => throw new NotImplementedException(); - - /// - public override double? GetMeasure(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the point count of the geometry. - /// - public override int? GetPointCount(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_NPoints(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new int?() : rdr.GetInt32(0); - } - } - } - - /// - /// Returns the point count of the geography. - /// - public override int? GetPointCount(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns a POINT guaranteed to lie on the geometry surface. - /// - public override DbGeometry GetPointOnSurface(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_PointOnSurface(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// returns the spatial type of the geometry. - /// - public override string GetSpatialTypeName(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT GeometryType(:p1)"; - return (string)cmd.ExecuteScalar(); - } - } - - /// - /// Returns the spatial type of the geography. - /// - public override string GetSpatialTypeName(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns the start point of the geometry. - /// - public override DbGeometry GetStartPoint(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_StartPoint(:p1)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns the start point of the geography. - /// - public override DbGeography GetStartPoint(DbGeography geographyValue) - => throw new NotImplementedException(); - - /// - /// Returns a point X coordinate. - /// - public override double? GetXCoordinate(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_X(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : rdr.GetDouble(0); - } - } - } - - /// - /// Returns a point Y coordinate. - /// - public override double? GetYCoordinate(DbGeometry geometryValue) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = "SELECT ST_Y(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : rdr.GetDouble(0); - } - } - } - - /// - /// Returns the index-nth interior ring of the geometry - /// - public override DbGeometry InteriorRingAt(DbGeometry geometryValue, int index) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Integer, index); - cmd.CommandText = "SELECT ST_InteriorRingN(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - ///Returns the intersection of two geometries. - /// - public override DbGeometry Intersection(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Intersection(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns the intersection of two geographies. - /// - public override DbGeography Intersection(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Returns TRUE if the Geometries/Geography "spatially intersect in 2D" - (share any portion of space) and FALSE if they don't (they are Disjoint). - /// - public override bool Intersects(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry); - cmd.CommandText = "SELECT ST_Intersects(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Returns TRUE if the Geometries/Geography "spatially intersect in 2D" - (share any portion of space) and FALSE if they don't (they are Disjoint). - /// For geography -- tolerance is 0.00001 meters (so any points that close are considered to intersect) - /// - public override bool Intersects(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Returns TRUE if the Geometries share space, are of the same dimension, but are not completely contained by each other. - /// - public override bool Overlaps(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Overlaps(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - public override DbGeometry PointAt(DbGeometry geometryValue, int index) - => throw new NotImplementedException(); - - /// - public override DbGeography PointAt(DbGeography geographyValue, int index) - => throw new NotImplementedException(); - - /// - /// Returns true if this Geometry is spatially related to anotherGeometry, - /// by testing for intersections between the Interior, Boundary and Exterior of the two geometries - /// - public override bool Relate(DbGeometry geometryValue, DbGeometry otherGeometry, string matrix) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.Parameters.AddWithValue("p3", NpgsqlDbType.Text, matrix); - cmd.CommandText = "SELECT ST_Relate(:p1,:p2,:p3)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Returns true if the given geometries represent the same geometry. Directionality is ignored. - /// - public override bool SpatialEquals(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Equals(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Returns true if the given geometries represent the same geometry. Directionality is ignored. - /// - public override bool SpatialEquals(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Returns a geometry that represents the portions of A and B that do not intersect. - /// It is called a symmetric difference because ST_SymDifference(A,B) = ST_SymDifference(B,A). - /// - public override DbGeometry SymmetricDifference(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_SymDifference(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns a geometry that represents the portions of A and B that do not intersect. - /// It is called a symmetric difference because ST_SymDifference(A,B) = ST_SymDifference(B,A). - /// - public override DbGeography SymmetricDifference(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Returns TRUE if the geometries have at least one point in common, but their interiors do not intersect. - /// - public override bool Touches(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Touches(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - /// - /// Returns a geometry that represents the point set union of the Geometries. - /// - public override DbGeometry Union(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Union(:p1,:p2)"; - return CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Returns a geometry that represents the point set union of the Geometries. - /// - public override DbGeography Union(DbGeography geographyValue, DbGeography otherGeography) - => throw new NotImplementedException(); - - /// - /// Returns true if the geometry A is completely inside geometry B - /// - public override bool Within(DbGeometry geometryValue, DbGeometry otherGeometry) - { - using (var cmd = _connection.CreateCommand()) - { - cmd.Parameters.AddWithValue("p1", NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = "SELECT ST_Within(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - NpgsqlConnection _connection; - - /// - /// Set the provider connection - /// - /// - public void SetConnection(NpgsqlConnection c) - { - _connection = c; - } - } -} diff --git a/EF6.PG/SqlGenerators/SqlSelectGenerator.cs b/EF6.PG/SqlGenerators/SqlSelectGenerator.cs index ed2616a..8a173e3 100644 --- a/EF6.PG/SqlGenerators/SqlSelectGenerator.cs +++ b/EF6.PG/SqlGenerators/SqlSelectGenerator.cs @@ -97,24 +97,32 @@ public override void BuildCommand(DbCommand command) ((NpgsqlCommand)command).UnknownResultTypeList = pe.Projection.Arguments.Select(a => ((PrimitiveType)((ColumnExpression)a).ColumnType.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.String).ToArray(); // We must treat sbyte and DateTimeOffset specially so the value is read correctly - if (pe.Projection.Arguments.Any(a => { - var kind = ((PrimitiveType)((ColumnExpression)a).ColumnType.EdmType).PrimitiveTypeKind; - return kind == PrimitiveTypeKind.SByte || kind == PrimitiveTypeKind.DateTimeOffset; - })) - { - ((NpgsqlCommand)command).ObjectResultTypes = pe.Projection.Arguments.Select(a => + if (pe.Projection.Arguments.Any(a => { var kind = ((PrimitiveType)((ColumnExpression)a).ColumnType.EdmType).PrimitiveTypeKind; - switch (kind) + return kind == PrimitiveTypeKind.SByte || kind == PrimitiveTypeKind.DateTimeOffset; + })) + { + try + { + ((NpgsqlCommand)command).ObjectResultTypes = pe.Projection.Arguments.Select(a => { - case PrimitiveTypeKind.SByte: - return typeof(sbyte); - case PrimitiveTypeKind.DateTimeOffset: - return typeof(DateTimeOffset); - default: - return null; - } - }).ToArray(); + var kind = ((PrimitiveType)((ColumnExpression)a).ColumnType.EdmType).PrimitiveTypeKind; + switch (kind) + { + case PrimitiveTypeKind.SByte: + return typeof(sbyte); + case PrimitiveTypeKind.DateTimeOffset: + return typeof(DateTimeOffset); + default: + return null; + } + }).ToArray(); + } + catch + { + throw new NotSupportedException("DateTimeOffset is not supported."); + } } } } diff --git a/EF6.PG/content/net45/App.config.transform b/EF6.PG/content/net45/App.config.transform deleted file mode 100644 index 1dad58e..0000000 --- a/EF6.PG/content/net45/App.config.transform +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/EF6.PG/content/net45/Web.config.transform b/EF6.PG/content/net45/Web.config.transform deleted file mode 100644 index 1dad58e..0000000 --- a/EF6.PG/content/net45/Web.config.transform +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/EF6.PG/content/net461/App.config.transform b/EF6.PG/content/net461/App.config.transform deleted file mode 100644 index 0899448..0000000 --- a/EF6.PG/content/net461/App.config.transform +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/EF6.PG/content/net461/Web.config.transform b/EF6.PG/content/net461/Web.config.transform deleted file mode 100644 index 0899448..0000000 --- a/EF6.PG/content/net461/Web.config.transform +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - -