diff --git a/.gitignore b/.gitignore index 9a262f9..f10af03 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.sln.docstates *.userprefs /*.nupkg +.idea/ .nuget/ [Bb]in/ [Bb]uild/ @@ -18,3 +19,4 @@ artifacts/ *.lock.json .build TestResult.xml +.vs/ diff --git a/.teamcity/EntityFramework6Npgsql/buildTypes/EntityFramework6Npgsql_Build.xml b/.teamcity/EntityFramework6Npgsql/buildTypes/EntityFramework6Npgsql_Build.xml deleted file mode 100644 index 80b1396..0000000 --- a/.teamcity/EntityFramework6Npgsql/buildTypes/EntityFramework6Npgsql_Build.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - Build - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.teamcity/EntityFramework6Npgsql/pluginData/plugin-settings.xml b/.teamcity/EntityFramework6Npgsql/pluginData/plugin-settings.xml deleted file mode 100644 index 784b770..0000000 --- a/.teamcity/EntityFramework6Npgsql/pluginData/plugin-settings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.teamcity/EntityFramework6Npgsql/project-config.xml b/.teamcity/EntityFramework6Npgsql/project-config.xml deleted file mode 100644 index 6c2ccfb..0000000 --- a/.teamcity/EntityFramework6Npgsql/project-config.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - EntityFramework6.Npgsql - Entity Framework 6/5 for PostgreSQL - - - - - - diff --git a/.teamcity/EntityFramework6Npgsql/vcsRoots/EntityFramework6Npgsql_Github.xml b/.teamcity/EntityFramework6Npgsql/vcsRoots/EntityFramework6Npgsql_Github.xml deleted file mode 100644 index 7c55983..0000000 --- a/.teamcity/EntityFramework6Npgsql/vcsRoots/EntityFramework6Npgsql_Github.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - https://github.com/npgsql/EntityFramework6.Npgsql.git - - - - - - - - - - - - - - diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..3c65479 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,14 @@ + + + + true + snupkg + + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..d3fa71d --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/EntityFramework6.Npgsql.Tests/App.config b/EF6.PG.Tests/App.config similarity index 71% rename from test/EntityFramework6.Npgsql.Tests/App.config rename to EF6.PG.Tests/App.config index 79c8fa4..f113db7 100644 --- a/test/EntityFramework6.Npgsql.Tests/App.config +++ b/EF6.PG.Tests/App.config @@ -28,17 +28,9 @@ - - - - - - - - - - + + - \ No newline at end of file + diff --git a/EF6.PG.Tests/EF6.PG.Tests.csproj b/EF6.PG.Tests/EF6.PG.Tests.csproj new file mode 100644 index 0000000..5f9b005 --- /dev/null +++ b/EF6.PG.Tests/EF6.PG.Tests.csproj @@ -0,0 +1,19 @@ + + + latest + net45;netcoreapp3.1 + EntityFramework6.Npgsql.Tests + EntityFramework6.Npgsql.Tests + + + + + + + + + + + + + diff --git a/test/EntityFramework6.Npgsql.Tests/EntityFrameworkBasicTests.cs b/EF6.PG.Tests/EntityFrameworkBasicTests.cs similarity index 71% rename from test/EntityFramework6.Npgsql.Tests/EntityFrameworkBasicTests.cs rename to EF6.PG.Tests/EntityFrameworkBasicTests.cs index e1e30fc..cb52e3d 100644 --- a/test/EntityFramework6.Npgsql.Tests/EntityFrameworkBasicTests.cs +++ b/EF6.PG.Tests/EntityFrameworkBasicTests.cs @@ -1,27 +1,4 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using Npgsql; +using Npgsql; using NUnit.Framework; using System; using System.Collections.Generic; @@ -33,6 +10,7 @@ using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; +using System.Diagnostics.CodeAnalysis; using NpgsqlTypes; namespace EntityFramework6.Npgsql.Tests @@ -82,6 +60,21 @@ public void InsertAndSelect() } } + [Test] + public void InsertAndSelectSchemaless() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.NoColumnsEntities.Add(new NoColumnsEntity()); + context.SaveChanges(); + } + + using (var context = new BloggingContext(ConnectionString)) + { + Assert.AreEqual(1, context.NoColumnsEntities.Count()); + } + } + [Test] public void SelectWithWhere() { @@ -621,8 +614,8 @@ public void TestScalarValuedStoredFunctions() CollectionAssert.AreEqual(localChangedIds, remoteChangedIds); } } - - [Test] + + [Test] public void TestScalarValuedStoredFunctions_with_null_StoreFunctionName() { using (var context = new BloggingContext(ConnectionString)) @@ -650,5 +643,276 @@ public void TestScalarValuedStoredFunctions_with_null_StoreFunctionName() Assert.That(echo, Is.EqualTo(1337)); } } + + [Test] + public void TestCastFunction() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + var varbitVal = "10011"; + + var blog = new Blog + { + Name = "_", + Posts = new List + { + new Post + { + Content = "Some post content", + Rating = 1, + Title = "Some post Title", + VarbitColumn = varbitVal + } + } + }; + context.Blogs.Add(blog); + context.SaveChanges(); + + Assert.IsTrue( + context.Posts.Select( + p => NpgsqlTypeFunctions.Cast(p.VarbitColumn, "varbit") == varbitVal).First()); + + Assert.IsTrue( + context.Posts.Select( + p => NpgsqlTypeFunctions.Cast(p.VarbitColumn, "varbit") == "10011").First()); + } + } + + [Test] + public void Test_issue_27_select_ef_generated_literals_from_inner_select() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + var blog = new Blog { Name = "Hello" }; + context.Users.Add(new Administrator { Blogs = new List { blog } }); + context.Users.Add(new Editor()); + context.SaveChanges(); + + var administrator = context.Users + .Where(x => x is Administrator) // Removing this changes the query to using a UNION which doesn't fail. + .Select( + x => new + { + // causes entity framework to emit a literal discriminator + Computed = x is Administrator + ? "I administrate" + : x is Editor + ? "I edit" + : "Unknown", + // causes an inner select to be emitted thus showing the issue + HasBlog = x.Blogs.Any() + }) + .First(); + + Assert.That(administrator.Computed, Is.EqualTo("I administrate")); + Assert.That(administrator.HasBlog, Is.True); + } + } + + [Test] + public void TestTableValuedStoredFunctions() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + // Add some data and query it back using Stored Function + context.Blogs.Add(new Blog + { + Name = "Some blog1 name", + Posts = new List() + }); + context.Blogs.Add(new Blog + { + Name = "Some blog2 name", + Posts = new List() + }); + context.SaveChanges(); + + // Query back + var query = from b in context.GetBlogsByName("blog1") + select b; + var list = query.ToList(); + + Assert.AreEqual(1, list.Count); + Assert.AreEqual("Some blog1 name", list[0].Name); + + // Query with projection + var query2 = from b in context.GetBlogsByName("blog1") + select new { b.Name, Something = 1 }; + var list2 = query2.ToList(); + Assert.AreEqual(1, list2.Count); + Assert.AreEqual("Some blog1 name", list2[0].Name); + Assert.AreEqual(1, list2[0].Something); + } + } + + [Test] + public void Test_string_type_inference_in_coalesce_statements() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + context.Blogs.Add(new Blog { Name = "Hello" }); + context.SaveChanges(); + + string stringValue = "string_value"; + var query = context.Blogs.Select(b => stringValue + "_postfix"); + var blogTitle = query.First(); + Assert.That(blogTitle, Is.EqualTo("string_value_postfix")); + Console.WriteLine(query.ToString()); + StringAssert.AreEqualIgnoringCase( + "SELECT COALESCE(@p__linq__0,E'') || E'_postfix' AS \"C1\" FROM \"dbo\".\"Blogs\" AS \"Extent1\"", + query.ToString()); + } + } + + [Test] + [SuppressMessage("ReSharper", "ConstantNullCoalescingCondition")] + public void Test_string_null_propagation() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + context.Blogs.Add(new Blog { Name = "Hello" }); + context.SaveChanges(); + + string stringValue = "string_value"; + var query = context.Blogs.Select(b => (stringValue ?? "default_value") + "_postfix"); + var blog_title = query.First(); + Assert.That(blog_title, Is.EqualTo("string_value_postfix")); + + 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 ||" + + " E'_postfix' AS \"C1\" FROM \"dbo\".\"Blogs\" AS \"Extent1\"", + query.ToString()); + } + } + + [Test] + [SuppressMessage("ReSharper", "ConstantNullCoalescingCondition")] + public void Test_string_multiple_null_propagation() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + context.Blogs.Add(new Blog { Name = "Hello" }); + context.SaveChanges(); + + string stringValue1 = "string_value1"; + string stringValue2 = "string_value2"; + string stringValue3 = "string_value3"; + + var query = context.Blogs.Select(b => (stringValue1 ?? stringValue2 ?? stringValue3) + "_postfix"); + var blog_title = query.First(); + Assert.That(blog_title, Is.EqualTo("string_value1_postfix")); + + 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" + + " (@p__linq__0) END || E'_postfix' AS \"C1\" FROM \"dbo\".\"Blogs\" AS \"Extent1\"", + query.ToString()); + } + } + + [Test] + public void Test_enum() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + context.ClrEnumEntities.Add( + new ClrEnumEntity + { + TestByte = TestByteEnum.Bar, + TestShort = TestShortEnum.Bar, + TestInt = TestIntEnum.Bar, + TestLong = TestLongEnum.Bar + }); + context.SaveChanges(); + + var query = context.ClrEnumEntities.Where( + x => x.TestByte == TestByteEnum.Bar + && x.TestShort == TestShortEnum.Bar + && x.TestInt == TestIntEnum.Bar + && x.TestLong == TestLongEnum.Bar); + + var result = query.First(); + Assert.That(result.TestByte, Is.EqualTo(TestByteEnum.Bar)); + Assert.That(result.TestShort, Is.EqualTo(TestShortEnum.Bar)); + Assert.That(result.TestInt, Is.EqualTo(TestIntEnum.Bar)); + Assert.That(result.TestLong, Is.EqualTo(TestLongEnum.Bar)); + } + } + + [Test] + public void Test_enum_composite_key() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + context.ClrEnumCompositeKeyEntities.Add( + new ClrEnumCompositeKeyEntity + { + TestByte = TestByteEnum.Bar, + TestShort = TestShortEnum.Bar, + TestInt = TestIntEnum.Bar, + TestLong = TestLongEnum.Bar + }); + context.SaveChanges(); + } + + using (var context = new BloggingContext(ConnectionString)) + { + var result = context.ClrEnumCompositeKeyEntities.Find( + TestByteEnum.Bar, + TestShortEnum.Bar, + TestIntEnum.Bar, + TestLongEnum.Bar); + + Assert.That(result, Is.Not.Null); + Assert.That(result.TestByte, Is.EqualTo(TestByteEnum.Bar)); + Assert.That(result.TestShort, Is.EqualTo(TestShortEnum.Bar)); + Assert.That(result.TestInt, Is.EqualTo(TestIntEnum.Bar)); + Assert.That(result.TestLong, Is.EqualTo(TestLongEnum.Bar)); + } + } + + [Test] + public void Test_non_composable_function() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + // Add some data and query it back using Stored Function + context.Blogs.Add(new Blog + { + Name = "Some blog1 name", + Posts = new List() + }); + context.SaveChanges(); + + // Query back + var nameParameter = new ObjectParameter("Name", "blog1"); + var blogs = ((IObjectContextAdapter)context).ObjectContext.ExecuteFunction("GetBlogsByName2", nameParameter).ToArray(); + + Assert.AreEqual(1, blogs.Length); + Assert.AreEqual("Some blog1 name", blogs[0].Name); + } + } + } } diff --git a/test/EntityFramework6.Npgsql.Tests/EntityFrameworkMigrationTests.cs b/EF6.PG.Tests/EntityFrameworkMigrationTests.cs similarity index 92% rename from test/EntityFramework6.Npgsql.Tests/EntityFrameworkMigrationTests.cs rename to EF6.PG.Tests/EntityFrameworkMigrationTests.cs index a0b768c..3fde9fb 100644 --- a/test/EntityFramework6.Npgsql.Tests/EntityFrameworkMigrationTests.cs +++ b/EF6.PG.Tests/EntityFrameworkMigrationTests.cs @@ -1,27 +1,4 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using Npgsql; +using Npgsql; using NUnit.Framework; using System; using System.Collections.Generic; @@ -151,13 +128,13 @@ public void CreateBloggingContext() expectedColumns.Remove((string)reader[0]); Assert.AreEqual("integer", (string)reader[1]); Assert.AreEqual("NO", (string)reader[2]); - Assert.AreEqual("0", (string)reader[3]); + Assert.That(string.IsNullOrEmpty(reader[3] as string)); break; case "UniqueId": expectedColumns.Remove((string)reader[0]); Assert.AreEqual("uuid", (string)reader[1]); Assert.AreEqual("NO", (string)reader[2]); - Assert.AreEqual("'00000000-0000-0000-0000-000000000000'::uuid", reader[3] as string); + Assert.That(string.IsNullOrEmpty(reader[3] as string)); //Assert.AreEqual("uuid_generate_v4()", reader[3] as string); break; case "Rating": @@ -355,10 +332,10 @@ public void CreateTableOperation() var statments = new NpgsqlMigrationSqlGenerator().Generate(operations, _backendVersion.ToString()); Assert.AreEqual(2, statments.Count()); if (_backendVersion.Major > 9 || (_backendVersion.Major == 9 && _backendVersion.Minor > 2)) - Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS someSchema", statments.ElementAt(0).Sql); + Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS \"someSchema\"", statments.ElementAt(0).Sql); else - Assert.AreEqual("CREATE SCHEMA someSchema", statments.ElementAt(0).Sql); - Assert.AreEqual("CREATE TABLE \"someSchema\".\"someTable\"(\"SomeString\" varchar(233) NOT NULL DEFAULT '',\"AnotherString\" text,\"SomeBytes\" bytea,\"SomeLong\" serial8,\"SomeDateTime\" timestamp)", statments.ElementAt(1).Sql); + Assert.AreEqual("CREATE SCHEMA \"someSchema\"", statments.ElementAt(0).Sql); + Assert.AreEqual("CREATE TABLE \"someSchema\".\"someTable\"(\"SomeString\" varchar(233) NOT NULL,\"AnotherString\" text,\"SomeBytes\" bytea,\"SomeLong\" serial8,\"SomeDateTime\" timestamp)", statments.ElementAt(1).Sql); } [Test] @@ -491,7 +468,7 @@ public void RenameIndexOperation() } else { - Assert.AreEqual("ALTER INDEX someSchema.\"someOldIndexName\" RENAME TO \"someNewIndexName\"", statements.ElementAt(0).Sql); + Assert.AreEqual("ALTER INDEX someSchema.\"someOldIndexName\" RENAME TO \"someNewIndexName\"", statements.ElementAt(0).Sql); } } @@ -503,10 +480,10 @@ public void MoveTableOperation() var statments = new NpgsqlMigrationSqlGenerator().Generate(operations, _backendVersion.ToString()); Assert.AreEqual(2, statments.Count()); if (_backendVersion.Major > 9 || (_backendVersion.Major == 9 && _backendVersion.Minor > 2)) - Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS someNewSchema", statments.ElementAt(0).Sql); + Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS \"someNewSchema\"", statments.ElementAt(0).Sql); else - Assert.AreEqual("CREATE SCHEMA someNewSchema", statments.ElementAt(0).Sql); - Assert.AreEqual("ALTER TABLE \"someOldSchema\".\"someTable\" SET SCHEMA someNewSchema", statments.ElementAt(1).Sql); + Assert.AreEqual("CREATE SCHEMA \"someNewSchema\"", statments.ElementAt(0).Sql); + Assert.AreEqual("ALTER TABLE \"someOldSchema\".\"someTable\" SET SCHEMA \"someNewSchema\"", statments.ElementAt(1).Sql); } [Test] @@ -517,10 +494,38 @@ public void MoveTableOperationNewSchemaIsNull() var statments = new NpgsqlMigrationSqlGenerator().Generate(operations, _backendVersion.ToString()); Assert.AreEqual(2, statments.Count()); if (_backendVersion.Major > 9 || (_backendVersion.Major == 9 && _backendVersion.Minor > 2)) - Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS dbo", statments.ElementAt(0).Sql); + Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS \"dbo\"", statments.ElementAt(0).Sql); + else + Assert.AreEqual("CREATE SCHEMA \"dbo\"", statments.ElementAt(0).Sql); + Assert.AreEqual("ALTER TABLE \"someOldSchema\".\"someTable\" SET SCHEMA \"dbo\"", statments.ElementAt(1).Sql); + } + + [Test] + public void MoveTableOperationPrequotedNewSchema() + { + var operations = new List(); + operations.Add(new MoveTableOperation("someOldSchema.someTable", "\"prequotedNewSchema\"")); + var statments = new NpgsqlMigrationSqlGenerator().Generate(operations, _backendVersion.ToString()); + Assert.AreEqual(2, statments.Count()); + if (_backendVersion.Major > 9 || (_backendVersion.Major == 9 && _backendVersion.Minor > 2)) + Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS \"prequotedNewSchema\"", statments.ElementAt(0).Sql); + else + Assert.AreEqual("CREATE SCHEMA \"prequotedNewSchema\"", statments.ElementAt(0).Sql); + Assert.AreEqual("ALTER TABLE \"someOldSchema\".\"someTable\" SET SCHEMA \"prequotedNewSchema\"", statments.ElementAt(1).Sql); + } + + [Test] + public void MoveTableOperationPrequotedOldSchema() + { + var operations = new List(); + operations.Add(new MoveTableOperation("\"prequotedOldSchema\".\"someTable\"", "newSchema")); + var statments = new NpgsqlMigrationSqlGenerator().Generate(operations, _backendVersion.ToString()); + Assert.AreEqual(2, statments.Count()); + if (_backendVersion.Major > 9 || (_backendVersion.Major == 9 && _backendVersion.Minor > 2)) + Assert.AreEqual("CREATE SCHEMA IF NOT EXISTS \"newSchema\"", statments.ElementAt(0).Sql); else - Assert.AreEqual("CREATE SCHEMA dbo", statments.ElementAt(0).Sql); - Assert.AreEqual("ALTER TABLE \"someOldSchema\".\"someTable\" SET SCHEMA dbo", statments.ElementAt(1).Sql); + Assert.AreEqual("CREATE SCHEMA \"newSchema\"", statments.ElementAt(0).Sql); + Assert.AreEqual("ALTER TABLE \"prequotedOldSchema\".\"someTable\" SET SCHEMA \"newSchema\"", statments.ElementAt(1).Sql); } [Test] diff --git a/test/EntityFramework6.Npgsql.Tests/FullTextSearchTests.cs b/EF6.PG.Tests/FullTextSearchTests.cs similarity index 95% rename from test/EntityFramework6.Npgsql.Tests/FullTextSearchTests.cs rename to EF6.PG.Tests/FullTextSearchTests.cs index e293da7..6d81f43 100644 --- a/test/EntityFramework6.Npgsql.Tests/FullTextSearchTests.cs +++ b/EF6.PG.Tests/FullTextSearchTests.cs @@ -1,27 +1,4 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using Npgsql; +using Npgsql; using NUnit.Framework; using System; using System.Collections.Generic; @@ -602,5 +579,31 @@ public void TsRewrite() Is.EqualTo(NpgsqlTsQuery.Parse("'b' & ( 'foo' | 'bar' )").ToString())); } } + + [Test] + public void TsQueryPhrase() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + var blog1 = new Blog + { + Name = "_" + }; + context.Blogs.Add(blog1); + context.SaveChanges(); + + var newQuery = context + .Blogs.Select(x => NpgsqlTextFunctions.TsQueryPhrase("b", "c")) + .FirstOrDefault(); + Assert.That(newQuery, Is.EqualTo("'b' <-> 'c'")); + + newQuery = context + .Blogs.Select(x => NpgsqlTextFunctions.TsQueryPhrase("b", "c", 10)) + .FirstOrDefault(); + Assert.That(newQuery, Is.EqualTo("'b' <10> 'c'")); + } + } } } diff --git a/test/EntityFramework6.Npgsql.Tests/NLogLoggingProvider.cs b/EF6.PG.Tests/NLogLoggingProvider.cs similarity index 59% rename from test/EntityFramework6.Npgsql.Tests/NLogLoggingProvider.cs rename to EF6.PG.Tests/NLogLoggingProvider.cs index e8940e9..509a7f3 100644 --- a/test/EntityFramework6.Npgsql.Tests/NLogLoggingProvider.cs +++ b/EF6.PG.Tests/NLogLoggingProvider.cs @@ -1,27 +1,4 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; +using System; using NLog; using Npgsql.Logging; diff --git a/EF6.PG.Tests/PatternMatchingTests.cs b/EF6.PG.Tests/PatternMatchingTests.cs new file mode 100644 index 0000000..6f83d75 --- /dev/null +++ b/EF6.PG.Tests/PatternMatchingTests.cs @@ -0,0 +1,141 @@ +using Npgsql; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Data.Entity; +using System.Linq; +using System.Text; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data.Entity.Core.Metadata.Edm; +using System.Data.Entity.Core.Objects; +using System.Data.Entity.Infrastructure; +using NpgsqlTypes; +using System.Text.RegularExpressions; + +namespace EntityFramework6.Npgsql.Tests +{ + class PatternMatchingTests : EntityFrameworkTestBase + { + [Test] + [TestCase("blog", "blog", "BLOG", TestName = "Case-sensitive")] + [TestCase("^blog$", "blog", "some \nblog\n name", TestName = "^ and $ match beginning and end")] + [TestCase("some .* name", "some blog name", "some \n name", TestName = ". matches all except \\n")] + [TestCase("some blog name", "some blog name", "someblogname", TestName = "Whitespace not ignored in pattern")] + public void MatchRegex(string pattern, string matchingInput, string mismatchingInput) + { + // Arrange + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + context.Blogs.Add(new Blog() { Name = matchingInput }); + context.Blogs.Add(new Blog() { Name = mismatchingInput }); + context.SaveChanges(); + } + + // Act + // Ensure correctness of a test case + var netMatchResult = Regex.IsMatch(matchingInput, pattern); + var netMismatchResult = Regex.IsMatch(mismatchingInput, pattern); + + List pgMatchResults; + List pgMismatchResults; + List pgMatchWithOptionsResults; + List pgMismatchWithOptionsResults; + using (var context = new BloggingContext(ConnectionString)) + { + pgMatchResults = (from b in context.Blogs + where NpgsqlTextFunctions.MatchRegex(b.Name, pattern) + select b.Name).ToList(); + + pgMismatchResults = (from b in context.Blogs + where !NpgsqlTextFunctions.MatchRegex(b.Name, pattern) + select b.Name).ToList(); + + pgMatchWithOptionsResults = (from b in context.Blogs + where NpgsqlTextFunctions.MatchRegex(b.Name, pattern, RegexOptions.None) + select b.Name).ToList(); + + pgMismatchWithOptionsResults = (from b in context.Blogs + where !NpgsqlTextFunctions.MatchRegex(b.Name, pattern, RegexOptions.None) + select b.Name).ToList(); + } + + // Assert + Assert.That(netMatchResult, Is.True); + Assert.That(netMismatchResult, Is.False); + + Assert.That(pgMatchResults.Count, Is.EqualTo(1)); + Assert.That(pgMatchResults[0], Is.EqualTo(matchingInput)); + Assert.That(pgMismatchResults.Count, Is.EqualTo(1)); + Assert.That(pgMismatchResults[0], Is.EqualTo(mismatchingInput)); + + Assert.That(pgMatchWithOptionsResults.Count, Is.EqualTo(1)); + Assert.That(pgMatchWithOptionsResults[0], Is.EqualTo(matchingInput)); + Assert.That(pgMismatchWithOptionsResults.Count, Is.EqualTo(1)); + Assert.That(pgMismatchWithOptionsResults[0], Is.EqualTo(mismatchingInput)); + } + + [Test] + [TestCase(RegexOptions.IgnoreCase, "some", "SOME", "placeholder", TestName = "IgnoreCase")] + [TestCase(RegexOptions.IgnorePatternWhitespace, "s o m e", "some", "s o m e", TestName = "IgnorePatternWhitespace")] + [TestCase(RegexOptions.Multiline, "^blog$", "some \nblog\n name", "placeholder", TestName = "Multiline")] + [TestCase(RegexOptions.Singleline, "some .* name", "some \n name", "placeholder", TestName = "Singleline")] + public void MatchRegexOptions(RegexOptions options, string pattern, string matchingInput, string mismatchingInput) + { + // Arrange + using (var context = new BloggingContext(ConnectionString)) + { + context.Database.Log = Console.Out.WriteLine; + + context.Blogs.Add(new Blog() { Name = matchingInput }); + context.Blogs.Add(new Blog() { Name = mismatchingInput }); + context.SaveChanges(); + } + + // Act + // Ensure correctness of a test case + var netMatchResult = Regex.IsMatch(matchingInput, pattern, options); + var netMismatchResult = Regex.IsMatch(mismatchingInput, pattern, options); + + List pgMatchResults; + List pgMismatchResults; + using (var context = new BloggingContext(ConnectionString)) + { + pgMatchResults = (from b in context.Blogs + where NpgsqlTextFunctions.MatchRegex(b.Name, pattern, options) + select b.Name).ToList(); + + pgMismatchResults = (from b in context.Blogs + where !NpgsqlTextFunctions.MatchRegex(b.Name, pattern, options) + select b.Name).ToList(); + } + + // Assert + Assert.That(netMatchResult, Is.True); + Assert.That(netMismatchResult, Is.False); + + Assert.That(pgMatchResults.Count, Is.EqualTo(1)); + Assert.That(pgMatchResults[0], Is.EqualTo(matchingInput)); + Assert.That(pgMismatchResults.Count, Is.EqualTo(1)); + Assert.That(pgMismatchResults[0], Is.EqualTo(mismatchingInput)); + } + + [Test] + [TestCase(RegexOptions.RightToLeft)] + [TestCase(RegexOptions.ECMAScript)] + public void MatchRegex_NotSupportedOption(RegexOptions options) + { + using (var context = new BloggingContext(ConnectionString)) + { + Assert.That(() => + { + var results = (from b in context.Blogs + where NpgsqlTextFunctions.MatchRegex(b.Name, "Some pattern", options) + select b.Name).ToList(); + }, Throws.InnerException.TypeOf()); + } + } + } +} diff --git a/test/EntityFramework6.Npgsql.Tests/Spatial/PostgisServiceTests.cs b/EF6.PG.Tests/Spatial/PostgisServiceTests.cs similarity index 99% rename from test/EntityFramework6.Npgsql.Tests/Spatial/PostgisServiceTests.cs rename to EF6.PG.Tests/Spatial/PostgisServiceTests.cs index 0783539..e4fa6bc 100644 --- a/test/EntityFramework6.Npgsql.Tests/Spatial/PostgisServiceTests.cs +++ b/EF6.PG.Tests/Spatial/PostgisServiceTests.cs @@ -1,7 +1,7 @@ using System; using Npgsql; +using Npgsql.LegacyPostgis; using Npgsql.Spatial; -using NpgsqlTypes; using NUnit.Framework; // ReSharper disable RedundantExplicitArrayCreation @@ -1321,6 +1321,7 @@ PostgisServices CreatePostgisServices() 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(); diff --git a/EF6.PG.Tests/Support/AssemblySetup.cs b/EF6.PG.Tests/Support/AssemblySetup.cs new file mode 100644 index 0000000..e503497 --- /dev/null +++ b/EF6.PG.Tests/Support/AssemblySetup.cs @@ -0,0 +1,30 @@ +using System.Data.Entity; +using NLog.Config; +using NLog.Targets; +using NUnit.Framework; +using Npgsql.Logging; +using EntityFramework6.Npgsql.Tests; +using EntityFramework6.Npgsql.Tests.Support; + +// ReSharper disable CheckNamespace + +[SetUpFixture] +public class AssemblySetup +{ + [OneTimeSetUp] + public void RegisterDbProvider() + { + var config = new LoggingConfiguration(); + var consoleTarget = new ConsoleTarget(); + consoleTarget.Layout = @"${message} ${exception:format=tostring}"; + config.AddTarget("console", consoleTarget); + var rule = new LoggingRule("*", NLog.LogLevel.Info, consoleTarget); + config.LoggingRules.Add(rule); + NLog.LogManager.Configuration = config; + + NpgsqlLogManager.Provider = new NLogLoggingProvider(); + NpgsqlLogManager.IsParameterLoggingEnabled = true; + + DbConfiguration.SetConfiguration(new TestDbConfiguration()); + } +} diff --git a/test/EntityFramework6.Npgsql.Tests/Support/CodeAnnotations.cs b/EF6.PG.Tests/Support/CodeAnnotations.cs similarity index 100% rename from test/EntityFramework6.Npgsql.Tests/Support/CodeAnnotations.cs rename to EF6.PG.Tests/Support/CodeAnnotations.cs diff --git a/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs b/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs new file mode 100644 index 0000000..5504281 --- /dev/null +++ b/EF6.PG.Tests/Support/EntityFrameworkTestBase.cs @@ -0,0 +1,407 @@ +using Npgsql; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Data.Common; +using System.Data.Entity; +using System.Linq; +using System.Text; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data.Entity.Core.Mapping; +using System.Data.Entity.Core.Metadata.Edm; +using System.Data.Entity.Core.Objects; +using System.Data.Entity.Infrastructure; +using NpgsqlTypes; + +// ReSharper disable once CheckNamespace +namespace EntityFramework6.Npgsql.Tests +{ + public abstract class EntityFrameworkTestBase : TestBase + { + [OneTimeSetUp] + public new void TestFixtureSetup() + { + using (var context = new BloggingContext(ConnectionString)) + { + if (context.Database.Exists()) + context.Database.Delete();//We delete to be 100% schema is synced + context.Database.Create(); + } + + // Create sequence for the IntComputedValue property. + using (var createSequenceConn = OpenConnection(ConnectionString)) + { + createSequenceConn.ExecuteNonQuery("create sequence blog_int_computed_value_seq"); + createSequenceConn.ExecuteNonQuery("alter table \"dbo\".\"Blogs\" alter column \"IntComputedValue\" set default nextval('blog_int_computed_value_seq');"); + createSequenceConn.ExecuteNonQuery("alter table \"dbo\".\"Posts\" alter column \"VarbitColumn\" type varbit using null"); + createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"StoredAddFunction\"(integer, integer) RETURNS integer AS $$ SELECT $1 + $2; $$ LANGUAGE SQL;"); + createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"StoredEchoFunction\"(integer) RETURNS integer AS $$ SELECT $1; $$ LANGUAGE SQL;"); + createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"GetBlogsByName\"(text) RETURNS TABLE(\"BlogId\" int, \"Name\" text, \"IntComputedValue\" int) as $$ select \"BlogId\", \"Name\", \"IntComputedValue\" from \"dbo\".\"Blogs\" where \"Name\" ilike '%' || $1 || '%' $$ LANGUAGE SQL;"); + createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"GetBlogsByName2\"(text) RETURNS TABLE(\"BlogId\" int, \"Name\" text, \"IntComputedValue\" int) as $$ select \"BlogId\", \"Name\", \"IntComputedValue\" from \"dbo\".\"Blogs\" where \"Name\" ilike '%' || $1 || '%' $$ LANGUAGE SQL;"); + } + } + + /// + /// Clean any previous entites before our test + /// + [SetUp] + protected void SetUp() + { + using (var context = new BloggingContext(ConnectionString)) + { + context.Blogs.RemoveRange(context.Blogs); + context.Posts.RemoveRange(context.Posts); + context.NoColumnsEntities.RemoveRange(context.NoColumnsEntities); + context.SaveChanges(); + } + } + } + + public class Blog + { + public int BlogId { get; set; } + public string Name { get; set; } + + public virtual List Posts { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public int IntComputedValue { get; set; } + } + + public class Post + { + public int PostId { get; set; } + public string Title { get; set; } + public string Content { get; set; } + public byte Rating { get; set; } + public DateTime CreationDate { get; set; } + public string VarbitColumn { get; set; } + public int BlogId { get; set; } + public virtual Blog Blog { get; set; } + } + + public class ClrEnumEntity + { + public int Id { get; set; } + public TestByteEnum TestByte { get; set; } + public TestShortEnum TestShort { get; set; } + public TestIntEnum TestInt { get; set; } + public TestLongEnum TestLong { get; set; } + } + + public class ClrEnumCompositeKeyEntity + { + [Key, Column(Order = 1)] + public TestByteEnum TestByte { get; set; } + + [Key, Column(Order = 2)] + public TestShortEnum TestShort { get; set; } + + [Key, Column(Order = 3)] + public TestIntEnum TestInt { get; set; } + + [Key, Column(Order = 4)] + public TestLongEnum TestLong { get; set; } + } + + public enum TestByteEnum : byte + { + Foo = 0, + Bar = 1 + } + + public enum TestShortEnum : short + { + Foo = 0, + Bar = 1 + } + + public enum TestIntEnum + { + Foo = 0, + Bar = 1 + } + + public enum TestLongEnum : long + { + Foo = 0, + Bar = 1 + } + + public class NoColumnsEntity + { + public int Id { get; set; } + } + + [Table("Users")] + public abstract class User + { + public int Id { get; set; } + + public IList Blogs { get; set; } + } + + [Table("Editors")] + public class Editor : User { } + + [Table("Administrators")] + public class Administrator : User { } + + public class BloggingContext : DbContext + { + public BloggingContext(string connection) + : base(new NpgsqlConnection(connection), CreateModel(new NpgsqlConnection(connection)), true) + { + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet NoColumnsEntities { get; set; } + public DbSet ClrEnumEntities { get; set; } + public DbSet ClrEnumCompositeKeyEntities { get; set; } + 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) + { + throw new NotSupportedException(); + } + + [DbFunction("BloggingContext", "StoredEchoFunction")] + public static int StoredEchoFunction(int value) + { + throw new NotSupportedException(); + } + + [DbFunction("BloggingContext", "GetBlogsByName")] + public IQueryable GetBlogsByName(string name) + { + ObjectParameter nameParameter = new ObjectParameter("Name", name); + + return ((IObjectContextAdapter)this).ObjectContext.CreateQuery( + $"[GetBlogsByName](@Name)", nameParameter); + } + + private static DbCompiledModel CreateModel(NpgsqlConnection connection) + { + var dbModelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest); + + // Import Sets + dbModelBuilder.Entity(); + dbModelBuilder.Entity(); + dbModelBuilder.Entity(); + dbModelBuilder.Entity(); + dbModelBuilder.Entity(); + dbModelBuilder.Entity(); + dbModelBuilder.Entity(); + dbModelBuilder.Entity(); + + // Import function + var dbModel = dbModelBuilder.Build(connection); + var edmType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32); + + var addFunc = EdmFunction.Create( + "ClrStoredAddFunction", + "BloggingContext", + DataSpace.SSpace, + new EdmFunctionPayload + { + ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, + Schema = "dbo", + IsComposable = true, + IsNiladic = false, + IsBuiltIn = false, + IsAggregate = false, + IsFromProviderManifest = true, + StoreFunctionName = "StoredAddFunction", + ReturnParameters = new[] + { + FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue) + }, + Parameters = new[] + { + FunctionParameter.Create("Value1", edmType, ParameterMode.In), + FunctionParameter.Create("Value2", edmType, ParameterMode.In) + } + }, + null); + dbModel.StoreModel.AddItem(addFunc); + + var echoFunc = EdmFunction.Create( + "StoredEchoFunction", + "BloggingContext", + DataSpace.SSpace, + new EdmFunctionPayload + { + ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, + Schema = "dbo", + IsComposable = true, + IsNiladic = false, + IsBuiltIn = false, + IsAggregate = false, + IsFromProviderManifest = true, + StoreFunctionName = null, // intentional + ReturnParameters = new[] + { + FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue) + }, + Parameters = new[] + { + FunctionParameter.Create("Value1", edmType, ParameterMode.In) + } + }, + null); + dbModel.StoreModel.AddItem(echoFunc); + + var stringStoreType = dbModel.ProviderManifest.GetStoreTypes().First(x => x.ClrEquivalentType == typeof(string)); + var modelBlogStoreType = dbModel.StoreModel.EntityTypes.First(x => x.Name == typeof(Blog).Name); + var rowType = RowType.Create( + modelBlogStoreType.Properties.Select(x => + { + var clone = EdmProperty.Create(x.Name, x.TypeUsage); + clone.CollectionKind = x.CollectionKind; + clone.ConcurrencyMode = x.ConcurrencyMode; + clone.IsFixedLength = x.IsFixedLength; + clone.IsMaxLength = x.IsMaxLength; + clone.IsUnicode = x.IsUnicode; + clone.MaxLength = x.MaxLength; + clone.Precision = x.Precision; + clone.Scale = x.Scale; + clone.StoreGeneratedPattern = x.StoreGeneratedPattern; + clone.SetMetadataProperties(x + .MetadataProperties + .Where(metadataProerty => !clone + .MetadataProperties + .Any(cloneMetadataProperty => cloneMetadataProperty.Name.Equals(metadataProerty.Name)))); + return clone; + }), + null); + + var getBlogsFunc = EdmFunction.Create( + "StoredGetBlogsFunction", + "BloggingContext", + DataSpace.SSpace, + new EdmFunctionPayload + { + ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, + Schema = "dbo", + IsComposable = true, + IsNiladic = false, + IsBuiltIn = false, + IsAggregate = false, + StoreFunctionName = "GetBlogsByName", + ReturnParameters = new[] + { + FunctionParameter.Create("ReturnType1", rowType.GetCollectionType(), ParameterMode.ReturnValue) + }, + Parameters = new[] + { + FunctionParameter.Create("Name", stringStoreType, ParameterMode.In) + } + }, + null); + dbModel.StoreModel.AddItem(getBlogsFunc); + + var stringPrimitiveType = PrimitiveType.GetEdmPrimitiveTypes().First(x => x.ClrEquivalentType == typeof(string)); + var modelBlogConceptualType = dbModel.ConceptualModel.EntityTypes.First(x => x.Name == typeof(Blog).Name); + EdmFunction getBlogsFuncModel = EdmFunction.Create( + "GetBlogsByName", + dbModel.ConceptualModel.Container.Name, + DataSpace.CSpace, + new EdmFunctionPayload + { + IsFunctionImport = true, + IsComposable = true, + Parameters = new[] + { + FunctionParameter.Create("Name", stringPrimitiveType, ParameterMode.In) + }, + ReturnParameters = new[] + { + FunctionParameter.Create("ReturnType1", modelBlogConceptualType.GetCollectionType(), ParameterMode.ReturnValue) + }, + EntitySets = new[] + { + dbModel.ConceptualModel.Container.EntitySets.First(x => x.ElementType == modelBlogConceptualType) + } + }, + null); + dbModel.ConceptualModel.Container.AddFunctionImport(getBlogsFuncModel); + + dbModel.ConceptualToStoreMapping.AddFunctionImportMapping(new FunctionImportMappingComposable( + getBlogsFuncModel, + getBlogsFunc, + new FunctionImportResultMapping(), + dbModel.ConceptualToStoreMapping)); + + + var getBlogs2Func = EdmFunction.Create( + "GetBlogsByName2", + "BloggingContext", + DataSpace.SSpace, + new EdmFunctionPayload + { + ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, + Schema = "dbo", + IsComposable = false, + IsNiladic = false, + IsBuiltIn = false, + IsAggregate = false, + StoreFunctionName = "GetBlogsByName2", + ReturnParameters = new[] + { + FunctionParameter.Create("ReturnType1", rowType.GetCollectionType(), ParameterMode.ReturnValue) + }, + Parameters = new[] + { + FunctionParameter.Create("Name", stringStoreType, ParameterMode.In) + } + }, + null); + dbModel.StoreModel.AddItem(getBlogs2Func); + + EdmFunction getBlogs2FuncModel = EdmFunction.Create( + "GetBlogsByName2", + dbModel.ConceptualModel.Container.Name, + DataSpace.CSpace, + new EdmFunctionPayload + { + IsFunctionImport = true, + IsComposable = false, + Parameters = new[] + { + FunctionParameter.Create("Name", stringPrimitiveType, ParameterMode.In) + }, + ReturnParameters = new[] + { + FunctionParameter.Create("ReturnType1", modelBlogConceptualType.GetCollectionType(), ParameterMode.ReturnValue) + }, + EntitySets = new[] + { + dbModel.ConceptualModel.Container.EntitySets.First(x => x.ElementType == modelBlogConceptualType) + } + }, + null); + dbModel.ConceptualModel.Container.AddFunctionImport(getBlogs2FuncModel); + + dbModel.ConceptualToStoreMapping.AddFunctionImportMapping(new FunctionImportMappingNonComposable( + getBlogs2FuncModel, + getBlogs2Func, + new FunctionImportResultMapping[] { }, + dbModel.ConceptualToStoreMapping)); + + + var compiledModel = dbModel.Compile(); + return compiledModel; + } + + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + //modelBuilder.Conventions.Add(new FunctionsConvention("dbo")); + base.OnModelCreating(modelBuilder); + } + } +} diff --git a/test/EntityFramework6.Npgsql.Tests/Support/TestBase.cs b/EF6.PG.Tests/Support/TestBase.cs similarity index 55% rename from test/EntityFramework6.Npgsql.Tests/Support/TestBase.cs rename to EF6.PG.Tests/Support/TestBase.cs index d11f508..cf23265 100644 --- a/test/EntityFramework6.Npgsql.Tests/Support/TestBase.cs +++ b/EF6.PG.Tests/Support/TestBase.cs @@ -1,27 +1,4 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; +using System; using NLog.Config; using NLog.Targets; using NLog; @@ -46,8 +23,6 @@ public abstract class TestBase string _connectionString; - static bool _loggingSetUp; - /// /// Unless the NPGSQL_TEST_DB environment variable is defined, this is used as the connection string for the /// test database. @@ -59,28 +34,9 @@ public abstract class TestBase [OneTimeSetUp] public virtual void TestFixtureSetup() { - SetupLogging(); _log.Debug("Connection string is: " + ConnectionString); } - protected virtual void SetupLogging() - { - var config = new LoggingConfiguration(); - var consoleTarget = new ConsoleTarget(); - consoleTarget.Layout = @"${message} ${exception:format=tostring}"; - config.AddTarget("console", consoleTarget); - var rule = new LoggingRule("*", NLog.LogLevel.Debug, consoleTarget); - config.LoggingRules.Add(rule); - NLog.LogManager.Configuration = config; - - if (!_loggingSetUp) - { - NpgsqlLogManager.Provider = new NLogLoggingProvider(); - NpgsqlLogManager.IsParameterLoggingEnabled = true; - _loggingSetUp = true; - } - } - #endregion #region Utilities for use by tests diff --git a/EF6.PG.Tests/Support/TestDbConfiguration.cs b/EF6.PG.Tests/Support/TestDbConfiguration.cs new file mode 100644 index 0000000..2276bed --- /dev/null +++ b/EF6.PG.Tests/Support/TestDbConfiguration.cs @@ -0,0 +1,15 @@ +using System; +using System.Data.Entity; +using Npgsql; + +namespace EntityFramework6.Npgsql.Tests.Support +{ + public class TestDbConfiguration : DbConfiguration + { + public TestDbConfiguration() + { + SetProviderFactory("Npgsql", NpgsqlFactory.Instance); + SetProviderServices("Npgsql", NpgsqlServices.Instance); + } + } +} diff --git a/test/EntityFramework6.Npgsql.Tests/Support/TestUtil.cs b/EF6.PG.Tests/Support/TestUtil.cs similarity index 79% rename from test/EntityFramework6.Npgsql.Tests/Support/TestUtil.cs rename to EF6.PG.Tests/Support/TestUtil.cs index b34eeab..5b46519 100644 --- a/test/EntityFramework6.Npgsql.Tests/Support/TestUtil.cs +++ b/EF6.PG.Tests/Support/TestUtil.cs @@ -1,27 +1,4 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; +using System; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; diff --git a/test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.ObjectLayer.EF6.cs b/EF6.PG.Tests/xmlModel/XmlTest.ObjectLayer.EF6.cs similarity index 100% rename from test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.ObjectLayer.EF6.cs rename to EF6.PG.Tests/xmlModel/XmlTest.ObjectLayer.EF6.cs diff --git a/test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.ObjectLayer.cs b/EF6.PG.Tests/xmlModel/XmlTest.ObjectLayer.cs similarity index 100% rename from test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.ObjectLayer.cs rename to EF6.PG.Tests/xmlModel/XmlTest.ObjectLayer.cs diff --git a/test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.csdl b/EF6.PG.Tests/xmlModel/XmlTest.csdl similarity index 100% rename from test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.csdl rename to EF6.PG.Tests/xmlModel/XmlTest.csdl diff --git a/test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.msl b/EF6.PG.Tests/xmlModel/XmlTest.msl similarity index 100% rename from test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.msl rename to EF6.PG.Tests/xmlModel/XmlTest.msl diff --git a/test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.ssdl b/EF6.PG.Tests/xmlModel/XmlTest.ssdl similarity index 100% rename from test/EntityFramework6.Npgsql.Tests/xmlModel/XmlTest.ssdl rename to EF6.PG.Tests/xmlModel/XmlTest.ssdl diff --git a/src/EntityFramework6.Npgsql/App.config b/EF6.PG/App.config similarity index 100% rename from src/EntityFramework6.Npgsql/App.config rename to EF6.PG/App.config diff --git a/EF6.PG/EF6.PG.csproj b/EF6.PG/EF6.PG.csproj new file mode 100644 index 0000000..b84b9bc --- /dev/null +++ b/EF6.PG/EF6.PG.csproj @@ -0,0 +1,56 @@ + + + PostgreSQL provider for Entity Framework 6 + Shay Rojansky;Emil Lenngren;Francisco Figueiredo Jr.;Kenji Uno + Copyright 2020 © The Npgsql Development Team + Npgsql + npgsql postgresql postgres data database entity framework ef orm + 6.4.3 + latest + net45;net461;netstandard21 + true + CS1591 + true + Npgsql + EntityFramework6.Npgsql + true + ../Npgsql.snk + true + true + https://github.com/npgsql/EntityFramework6.Npgsql + postgresql.png + PostgreSQL + git + git://github.com/npgsql/EntityFramework6.Npgsql + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EF6.PG/NpgsqlConnectionFactory.cs b/EF6.PG/NpgsqlConnectionFactory.cs new file mode 100644 index 0000000..1d5e499 --- /dev/null +++ b/EF6.PG/NpgsqlConnectionFactory.cs @@ -0,0 +1,21 @@ +using System.Data.Common; +using System.Data.Entity.Infrastructure; + +namespace Npgsql +{ + /// + /// Instances of this class are used to create DbConnection objects for Postgresql + /// + public class NpgsqlConnectionFactory : IDbConnectionFactory + { + /// + /// Creates a connection for Postgresql for the given connection string. + /// + /// The connection string. + /// An initialized DbConnection. + public DbConnection CreateConnection(string nameOrConnectionString) + { + return new NpgsqlConnection(nameOrConnectionString); + } + } +} diff --git a/src/EntityFramework6.Npgsql/NpgsqlMigrationSqlGenerator.cs b/EF6.PG/NpgsqlMigrationSqlGenerator.cs similarity index 51% rename from src/EntityFramework6.Npgsql/NpgsqlMigrationSqlGenerator.cs rename to EF6.PG/NpgsqlMigrationSqlGenerator.cs index e00aa01..c3f26e0 100644 --- a/src/EntityFramework6.Npgsql/NpgsqlMigrationSqlGenerator.cs +++ b/EF6.PG/NpgsqlMigrationSqlGenerator.cs @@ -1,27 +1,4 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; +using System; using System.Collections.Generic; using System.Data.Entity.Migrations.Model; using System.Data.Entity.Migrations.Sql; @@ -30,6 +7,7 @@ using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Spatial; using System.Linq; +using JetBrains.Annotations; namespace Npgsql { @@ -38,10 +16,10 @@ namespace Npgsql /// public class NpgsqlMigrationSqlGenerator : MigrationSqlGenerator { - List migrationStatments; - private List addedSchemas; - private List addedExtensions; - private Version serverVersion; + List _migrationStatments; + List _addedSchemas; + List _addedExtensions; + Version _serverVersion; /// /// Generates the migration sql. @@ -49,117 +27,76 @@ public class NpgsqlMigrationSqlGenerator : MigrationSqlGenerator /// The operations in the migration /// The provider manifest token used for server versioning. public override IEnumerable Generate( - IEnumerable migrationOperations, string providerManifestToken) + [NotNull] IEnumerable migrationOperations, + [NotNull] string providerManifestToken) { - migrationStatments = new List(); - addedSchemas = new List(); - addedExtensions = new List(); - serverVersion = new Version(providerManifestToken); + _migrationStatments = new List(); + _addedSchemas = new List(); + _addedExtensions = new List(); + _serverVersion = new Version(providerManifestToken); Convert(migrationOperations); - return migrationStatments; + return _migrationStatments; } #region MigrationOperation to MigrationStatement converters #region General - protected virtual void Convert(IEnumerable operations) + protected virtual void Convert([NotNull] IEnumerable operations) { foreach (var migrationOperation in operations) { - if (migrationOperation is AddColumnOperation) - { - Convert(migrationOperation as AddColumnOperation); - } - else if (migrationOperation is AlterColumnOperation) - { - Convert(migrationOperation as AlterColumnOperation); - } - else if (migrationOperation is CreateTableOperation) - { - Convert(migrationOperation as CreateTableOperation); - } - else if (migrationOperation is DropForeignKeyOperation) - { - Convert(migrationOperation as DropForeignKeyOperation); - } - else if (migrationOperation is DropTableOperation) - { - Convert(migrationOperation as DropTableOperation); - } - else if (migrationOperation is MoveTableOperation) - { - Convert(migrationOperation as MoveTableOperation); - } - else if (migrationOperation is RenameTableOperation) - { - Convert(migrationOperation as RenameTableOperation); - } - else if (migrationOperation is AddForeignKeyOperation) - { - Convert(migrationOperation as AddForeignKeyOperation); - } - else if (migrationOperation is DropIndexOperation) - { - Convert(migrationOperation as DropIndexOperation); - } - else if (migrationOperation is SqlOperation) - { - AddStatment((migrationOperation as SqlOperation).Sql, (migrationOperation as SqlOperation).SuppressTransaction); - } - else if (migrationOperation is AddPrimaryKeyOperation) - { - Convert(migrationOperation as AddPrimaryKeyOperation); - } - else if (migrationOperation is CreateIndexOperation) - { - Convert(migrationOperation as CreateIndexOperation); - } - else if (migrationOperation is RenameIndexOperation) - { - Convert(migrationOperation as RenameIndexOperation); - } - else if (migrationOperation is DropColumnOperation) - { - Convert(migrationOperation as DropColumnOperation); - } - else if (migrationOperation is DropPrimaryKeyOperation) - { - Convert(migrationOperation as DropPrimaryKeyOperation); - } - else if (migrationOperation is HistoryOperation) - { - Convert(migrationOperation as HistoryOperation); - } - else if (migrationOperation is RenameColumnOperation) - { - Convert(migrationOperation as RenameColumnOperation); - } - else if (migrationOperation is UpdateDatabaseOperation) - { - Convert((migrationOperation as UpdateDatabaseOperation).Migrations as IEnumerable); - } + if (migrationOperation is AddColumnOperation operation) + Convert(operation); + else if (migrationOperation is AlterColumnOperation columnOperation) + Convert(columnOperation); + else if (migrationOperation is CreateTableOperation tableOperation) + Convert(tableOperation); + else if (migrationOperation is DropForeignKeyOperation keyOperation) + Convert(keyOperation); + else if (migrationOperation is DropTableOperation dropTableOperation) + Convert(dropTableOperation); + else if (migrationOperation is MoveTableOperation moveTableOperation) + Convert(moveTableOperation); + else if (migrationOperation is RenameTableOperation renameTableOperation) + Convert(renameTableOperation); + else if (migrationOperation is AddForeignKeyOperation foreignKeyOperation) + Convert(foreignKeyOperation); + else if (migrationOperation is DropIndexOperation indexOperation) + Convert(indexOperation); + else if (migrationOperation is SqlOperation sqlOperation) + AddStatment(sqlOperation.Sql, sqlOperation.SuppressTransaction); + else if (migrationOperation is AddPrimaryKeyOperation primaryKeyOperation) + Convert(primaryKeyOperation); + else if (migrationOperation is CreateIndexOperation createIndexOperation) + Convert(createIndexOperation); + else if (migrationOperation is RenameIndexOperation renameIndexOperation) + Convert(renameIndexOperation); + else if (migrationOperation is DropColumnOperation dropColumnOperation) + Convert(dropColumnOperation); + else if (migrationOperation is DropPrimaryKeyOperation dropPrimaryKeyOperation) + Convert(dropPrimaryKeyOperation); + else if (migrationOperation is HistoryOperation historyOperation) + Convert(historyOperation); + else if (migrationOperation is RenameColumnOperation renameColumnOperation) + Convert(renameColumnOperation); + else if (migrationOperation is UpdateDatabaseOperation databaseOperation) + Convert(databaseOperation.Migrations as IEnumerable); else - { throw new NotImplementedException("Unhandled MigrationOperation " + migrationOperation.GetType().Name + " in " + GetType().Name); - } } } - private void AddStatment(string sql, bool suppressTransacion = false) - { - migrationStatments.Add(new MigrationStatement - { - Sql = sql, - SuppressTransaction = suppressTransacion, - BatchTerminator = ";" - }); - } + void AddStatment(string sql, bool suppressTransacion = false) + => _migrationStatments.Add(new MigrationStatement + { + Sql = sql, + SuppressTransaction = suppressTransacion, + BatchTerminator = ";" + }); - private void AddStatment(StringBuilder sql, bool suppressTransacion = false) - { - AddStatment(sql.ToString(), suppressTransacion); - } + void AddStatment(StringBuilder sql, bool suppressTransacion = false) + => AddStatment(sql.ToString(), suppressTransacion); #endregion @@ -170,7 +107,7 @@ protected virtual void Convert(HistoryOperation historyOperation) foreach (var command in historyOperation.CommandTrees) { var npgsqlCommand = new NpgsqlCommand(); - NpgsqlServices.Instance.TranslateCommandTree(serverVersion, command, npgsqlCommand, false); + NpgsqlServices.Instance.TranslateCommandTree(_serverVersion, command, npgsqlCommand, false); AddStatment(npgsqlCommand.CommandText); } } @@ -181,12 +118,10 @@ protected virtual void Convert(HistoryOperation historyOperation) protected virtual void Convert(CreateTableOperation createTableOperation) { - StringBuilder sql = new StringBuilder(); - int dotIndex = createTableOperation.Name.IndexOf('.'); + var sql = new StringBuilder(); + var dotIndex = createTableOperation.Name.IndexOf('.'); if (dotIndex != -1) - { CreateSchema(createTableOperation.Name.Remove(dotIndex)); - } sql.Append("CREATE TABLE "); AppendTableName(createTableOperation.Name, sql); @@ -222,7 +157,7 @@ protected virtual void Convert(CreateTableOperation createTableOperation) protected virtual void Convert(DropTableOperation dropTableOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("DROP TABLE "); AppendTableName(dropTableOperation.Name, sql); AddStatment(sql); @@ -230,7 +165,7 @@ protected virtual void Convert(DropTableOperation dropTableOperation) protected virtual void Convert(RenameTableOperation renameTableOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(renameTableOperation.Name, sql); sql.Append(" RENAME TO "); @@ -238,23 +173,21 @@ protected virtual void Convert(RenameTableOperation renameTableOperation) AddStatment(sql); } - private void CreateSchema(string schemaName) + void CreateSchema(string schemaName) { - if (schemaName == "public" || addedSchemas.Contains(schemaName)) + if (schemaName == "public" || _addedSchemas.Contains(schemaName)) return; - addedSchemas.Add(schemaName); - if (serverVersion.Major > 9 || (serverVersion.Major == 9 && serverVersion.Minor >= 3)) - { - AddStatment("CREATE SCHEMA IF NOT EXISTS " + schemaName); - } + _addedSchemas.Add(schemaName); + if (_serverVersion.Major > 9 || (_serverVersion.Major == 9 && _serverVersion.Minor >= 3)) + AddStatment("CREATE SCHEMA IF NOT EXISTS " + QuoteIdentifier(schemaName)); else { //TODO: CREATE PROCEDURE that checks if schema already exists on servers < 9.3 - AddStatment("CREATE SCHEMA " + schemaName); + AddStatment("CREATE SCHEMA " + QuoteIdentifier(schemaName)); } } - //private void CreateExtension(string exensionName) + //void CreateExtension(string exensionName) //{ // //This is compatible only with server 9.1+ // if (serverVersion.Major > 9 || (serverVersion.Major == 9 && serverVersion.Minor >= 1)) @@ -268,22 +201,23 @@ private void CreateSchema(string schemaName) protected virtual void Convert(MoveTableOperation moveTableOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); var newSchema = moveTableOperation.NewSchema ?? "dbo"; CreateSchema(newSchema); sql.Append("ALTER TABLE "); AppendTableName(moveTableOperation.Name, sql); sql.Append(" SET SCHEMA "); - sql.Append(newSchema); + AppendQuotedIdentifier(newSchema, sql); AddStatment(sql); } #endregion #region Columns + protected virtual void Convert(AddColumnOperation addColumnOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(addColumnOperation.Table, sql); sql.Append(" ADD "); @@ -293,7 +227,7 @@ protected virtual void Convert(AddColumnOperation addColumnOperation) protected virtual void Convert(DropColumnOperation dropColumnOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(dropColumnOperation.Table, sql); sql.Append(" DROP COLUMN \""); @@ -304,7 +238,7 @@ protected virtual void Convert(DropColumnOperation dropColumnOperation) protected virtual void Convert(AlterColumnOperation alterColumnOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); //TYPE AppendAlterColumn(alterColumnOperation, sql); @@ -359,13 +293,11 @@ protected virtual void Convert(AlterColumnOperation alterColumnOperation) } } else - { sql.Append(" DROP DEFAULT"); - } AddStatment(sql); } - private void AppendAlterColumn(AlterColumnOperation alterColumnOperation, StringBuilder sql) + void AppendAlterColumn(AlterColumnOperation alterColumnOperation, StringBuilder sql) { sql.Append("ALTER TABLE "); AppendTableName(alterColumnOperation.Table, sql); @@ -376,7 +308,7 @@ private void AppendAlterColumn(AlterColumnOperation alterColumnOperation, String protected virtual void Convert(RenameColumnOperation renameColumnOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(renameColumnOperation.Table, sql); sql.Append(" RENAME COLUMN \""); @@ -393,7 +325,7 @@ protected virtual void Convert(RenameColumnOperation renameColumnOperation) protected virtual void Convert(AddForeignKeyOperation addForeignKeyOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(addForeignKeyOperation.DependentTable, sql); sql.Append(" ADD CONSTRAINT \""); @@ -419,21 +351,19 @@ protected virtual void Convert(AddForeignKeyOperation addForeignKeyOperation) sql.Append(")"); if (addForeignKeyOperation.CascadeDelete) - { sql.Append(" ON DELETE CASCADE"); - } AddStatment(sql); } protected virtual void Convert(DropForeignKeyOperation dropForeignKeyOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(dropForeignKeyOperation.DependentTable, sql); - if (serverVersion.Major < 9) - sql.Append(" DROP CONSTRAINT \"");//TODO: http://piecesformthewhole.blogspot.com/2011/04/dropping-foreign-key-if-it-exists-in.html ? - else - sql.Append(" DROP CONSTRAINT IF EXISTS \""); + sql.Append(_serverVersion.Major < 9 + ? " DROP CONSTRAINT \"" //TODO: http://piecesformthewhole.blogspot.com/2011/04/dropping-foreign-key-if-it-exists-in.html ? + : " DROP CONSTRAINT IF EXISTS \"" + ); sql.Append(dropForeignKeyOperation.Name); sql.Append('"'); AddStatment(sql); @@ -441,7 +371,7 @@ protected virtual void Convert(DropForeignKeyOperation dropForeignKeyOperation) protected virtual void Convert(CreateIndexOperation createIndexOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("CREATE "); if (createIndexOperation.IsUnique) @@ -465,16 +395,11 @@ protected virtual void Convert(CreateIndexOperation createIndexOperation) protected virtual void Convert(RenameIndexOperation renameIndexOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); - if (serverVersion.Major > 9 || (serverVersion.Major == 9 && serverVersion.Minor >= 2)) - { - sql.Append("ALTER INDEX IF EXISTS "); - } - else - { - sql.Append("ALTER INDEX "); - } + sql.Append(_serverVersion.Major > 9 || (_serverVersion.Major == 9 && _serverVersion.Minor >= 2) + ? "ALTER INDEX IF EXISTS " + : "ALTER INDEX "); sql.Append(GetSchemaNameFromFullTableName(renameIndexOperation.Table)); sql.Append(".\""); @@ -485,13 +410,11 @@ protected virtual void Convert(RenameIndexOperation renameIndexOperation) AddStatment(sql); } - private string GetSchemaNameFromFullTableName(string tableFullName) + string GetSchemaNameFromFullTableName(string tableFullName) { - int dotIndex = tableFullName.IndexOf('.'); - if (dotIndex != -1) - return tableFullName.Remove(dotIndex); - else - return "dto";//TODO: Check always setting dto schema if no schema in table name is not bug + var dotIndex = tableFullName.IndexOf('.'); + return dotIndex != -1 ? tableFullName.Remove(dotIndex) : "dto"; + //TODO: Check always setting dto schema if no schema in table name is not bug } /// @@ -499,18 +422,15 @@ private string GetSchemaNameFromFullTableName(string tableFullName) /// /// /// - private string GetTableNameFromFullTableName(string tableName) + string GetTableNameFromFullTableName(string tableName) { - int dotIndex = tableName.IndexOf('.'); - if (dotIndex != -1) - return tableName.Substring(dotIndex + 1); - else - return tableName; + var dotIndex = tableName.IndexOf('.'); + return dotIndex != -1 ? tableName.Substring(dotIndex + 1) : tableName; } protected virtual void Convert(DropIndexOperation dropIndexOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("DROP INDEX IF EXISTS "); sql.Append(GetSchemaNameFromFullTableName(dropIndexOperation.Table)); sql.Append(".\""); @@ -521,7 +441,7 @@ protected virtual void Convert(DropIndexOperation dropIndexOperation) protected virtual void Convert(AddPrimaryKeyOperation addPrimaryKeyOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(addPrimaryKeyOperation.Table, sql); sql.Append(" ADD CONSTRAINT \""); @@ -542,7 +462,7 @@ protected virtual void Convert(AddPrimaryKeyOperation addPrimaryKeyOperation) protected virtual void Convert(DropPrimaryKeyOperation dropPrimaryKeyOperation) { - StringBuilder sql = new StringBuilder(); + var sql = new StringBuilder(); sql.Append("ALTER TABLE "); AppendTableName(dropPrimaryKeyOperation.Table, sql); sql.Append(" DROP CONSTRAINT \""); @@ -557,7 +477,42 @@ protected virtual void Convert(DropPrimaryKeyOperation dropPrimaryKeyOperation) #region Misc functions - private void AppendColumn(ColumnModel column, StringBuilder sql) + /// + /// Quotes an identifier for Postgres SQL and appends it to a + /// + /// The identifier to be quoted. + /// The used for building the query. + /// The quoted identifier. + void AppendQuotedIdentifier(string identifier, StringBuilder builder) + { + if (string.IsNullOrEmpty(identifier)) + throw new ArgumentException("Value cannot be null or empty", nameof(identifier)); + + if (identifier[identifier.Length - 1] == '"' && identifier[0] == '"') + builder.Append(identifier); + else + { + builder.Append('"'); + builder.Append(identifier); + builder.Append('"'); + } + } + + /// + /// Quotes an identifier for Postgres SQL + /// + /// The identifier to be quoted. + /// The quoted identifier. + string QuoteIdentifier(string identifier) + { + if (string.IsNullOrEmpty(identifier)) + throw new ArgumentException("Value cannot be null or empty", nameof(identifier)); + + return identifier[identifier.Length - 1] == '"' && identifier[0] == '"' + ? identifier : $"\"{identifier}\""; + } + + void AppendColumn(ColumnModel column, StringBuilder sql) { sql.Append('"'); sql.Append(column.Name); @@ -581,190 +536,169 @@ private void AppendColumn(ColumnModel column, StringBuilder sql) { switch (column.Type) { - case PrimitiveTypeKind.Guid: - //CreateExtension("uuid-ossp"); - //If uuid-ossp is not enabled migrations throw exception - AddStatment("select * from uuid_generate_v4()"); - sql.Append(" DEFAULT uuid_generate_v4()"); - break; - case PrimitiveTypeKind.Byte: - case PrimitiveTypeKind.SByte: - case PrimitiveTypeKind.Int16: - case PrimitiveTypeKind.Int32: - case PrimitiveTypeKind.Int64: - //TODO: Add support for setting "SERIAL" - break; + case PrimitiveTypeKind.Guid: + //CreateExtension("uuid-ossp"); + //If uuid-ossp is not enabled migrations throw exception + AddStatment("select * from uuid_generate_v4()"); + sql.Append(" DEFAULT uuid_generate_v4()"); + break; + case PrimitiveTypeKind.Byte: + case PrimitiveTypeKind.SByte: + case PrimitiveTypeKind.Int16: + case PrimitiveTypeKind.Int32: + case PrimitiveTypeKind.Int64: + //TODO: Add support for setting "SERIAL" + break; } } - else if (column.IsNullable != null - && !column.IsNullable.Value - && (column.StoreType == null || - (column.StoreType.IndexOf("rowversion", StringComparison.OrdinalIgnoreCase) == -1))) - { - sql.Append(" DEFAULT "); - AppendValue(column.ClrDefaultValue, sql); - } } - private void AppendColumnType(ColumnModel column, StringBuilder sql, bool setSerial) + void AppendColumnType(ColumnModel column, StringBuilder sql, bool setSerial) { if (column.StoreType != null) { sql.Append(column.StoreType); return; } + switch (column.Type) { - case PrimitiveTypeKind.Binary: - sql.Append("bytea"); - break; - case PrimitiveTypeKind.Boolean: - sql.Append("boolean"); - break; - case PrimitiveTypeKind.DateTime: - if (column.Precision != null) - sql.Append("timestamp(" + column.Precision + ")"); - else - sql.Append("timestamp"); - break; - case PrimitiveTypeKind.Decimal: - //TODO: Check if inside min/max - if (column.Precision == null && column.Scale == null) - { - sql.Append("numeric"); - } - else - { - sql.Append("numeric("); - sql.Append(column.Precision ?? 19); - sql.Append(','); - sql.Append(column.Scale ?? 4); - sql.Append(')'); - } - break; - case PrimitiveTypeKind.Double: - sql.Append("float8"); - break; - case PrimitiveTypeKind.Guid: - sql.Append("uuid"); - break; - case PrimitiveTypeKind.Single: - sql.Append("float4"); - break; - case PrimitiveTypeKind.Byte://postgres doesn't support sbyte :( - case PrimitiveTypeKind.SByte://postgres doesn't support sbyte :( - case PrimitiveTypeKind.Int16: - if (setSerial) - sql.Append(column.IsIdentity ? "serial2" : "int2"); - else - sql.Append("int2"); - break; - case PrimitiveTypeKind.Int32: - if (setSerial) - sql.Append(column.IsIdentity ? "serial4" : "int4"); - else - sql.Append("int4"); - break; - case PrimitiveTypeKind.Int64: - if (setSerial) - sql.Append(column.IsIdentity ? "serial8" : "int8"); - else - sql.Append("int8"); - break; - case PrimitiveTypeKind.String: - if (column.IsFixedLength.HasValue && - column.IsFixedLength.Value && - column.MaxLength.HasValue) - { - sql.AppendFormat("char({0})",column.MaxLength.Value); - } - else if (column.MaxLength.HasValue) - { - sql.AppendFormat("varchar({0})", column.MaxLength); - } - else - { - sql.Append("text"); - } - break; - case PrimitiveTypeKind.Time: - if (column.Precision != null) - { - sql.Append("interval("); - sql.Append(column.Precision); - sql.Append(')'); - } - else - { - sql.Append("interval"); - } - break; - case PrimitiveTypeKind.DateTimeOffset: - if (column.Precision != null) - { - sql.Append("timestamptz("); - sql.Append(column.Precision); - sql.Append(')'); - } - else - { - sql.Append("timestamptz"); - } - break; - case PrimitiveTypeKind.Geometry: - sql.Append("point"); - break; - //case PrimitiveTypeKind.Geography: - // break; - //case PrimitiveTypeKind.GeometryPoint: - // break; - //case PrimitiveTypeKind.GeometryLineString: - // break; - //case PrimitiveTypeKind.GeometryPolygon: - // break; - //case PrimitiveTypeKind.GeometryMultiPoint: - // break; - //case PrimitiveTypeKind.GeometryMultiLineString: - // break; - //case PrimitiveTypeKind.GeometryMultiPolygon: - // break; - //case PrimitiveTypeKind.GeometryCollection: - // break; - //case PrimitiveTypeKind.GeographyPoint: - // break; - //case PrimitiveTypeKind.GeographyLineString: - // break; - //case PrimitiveTypeKind.GeographyPolygon: - // break; - //case PrimitiveTypeKind.GeographyMultiPoint: - // break; - //case PrimitiveTypeKind.GeographyMultiLineString: - // break; - //case PrimitiveTypeKind.GeographyMultiPolygon: - // break; - //case PrimitiveTypeKind.GeographyCollection: - // break; - default: - throw new ArgumentException("Unhandled column type:" + column.Type); - } - } - - private void AppendTableName(string tableName, StringBuilder sql) - { - int dotIndex = tableName.IndexOf('.'); + case PrimitiveTypeKind.Binary: + sql.Append("bytea"); + break; + case PrimitiveTypeKind.Boolean: + sql.Append("boolean"); + break; + case PrimitiveTypeKind.DateTime: + sql.Append(column.Precision != null + ? $"timestamp({column.Precision})" + : "timestamp" + ); + break; + case PrimitiveTypeKind.Decimal: + //TODO: Check if inside min/max + if (column.Precision == null && column.Scale == null) + sql.Append("numeric"); + else + { + sql.Append("numeric("); + sql.Append(column.Precision ?? 19); + sql.Append(','); + sql.Append(column.Scale ?? 4); + sql.Append(')'); + } + break; + case PrimitiveTypeKind.Double: + sql.Append("float8"); + break; + case PrimitiveTypeKind.Guid: + sql.Append("uuid"); + break; + case PrimitiveTypeKind.Single: + sql.Append("float4"); + break; + case PrimitiveTypeKind.Byte://postgres doesn't support sbyte :( + case PrimitiveTypeKind.SByte://postgres doesn't support sbyte :( + case PrimitiveTypeKind.Int16: + sql.Append(setSerial + ? column.IsIdentity ? "serial2" : "int2" + : "int2" + ); + break; + case PrimitiveTypeKind.Int32: + sql.Append(setSerial + ? column.IsIdentity ? "serial4" : "int4" + : "int4" + ); + break; + case PrimitiveTypeKind.Int64: + sql.Append(setSerial + ? column.IsIdentity ? "serial8" : "int8" + : "int8" + ); + break; + case PrimitiveTypeKind.String: + if (column.IsFixedLength.HasValue && + column.IsFixedLength.Value && + column.MaxLength.HasValue) + { + sql.Append($"char({column.MaxLength.Value})"); + } + else if (column.MaxLength.HasValue) + sql.Append($"varchar({column.MaxLength})"); + else + sql.Append("text"); + break; + case PrimitiveTypeKind.Time: + if (column.Precision != null) + { + sql.Append("interval("); + sql.Append(column.Precision); + sql.Append(')'); + } + else + sql.Append("interval"); + break; + case PrimitiveTypeKind.DateTimeOffset: + if (column.Precision != null) + { + sql.Append("timestamptz("); + sql.Append(column.Precision); + sql.Append(')'); + } + else + { + sql.Append("timestamptz"); + } + break; + case PrimitiveTypeKind.Geometry: + sql.Append("point"); + break; + //case PrimitiveTypeKind.Geography: + // break; + //case PrimitiveTypeKind.GeometryPoint: + // break; + //case PrimitiveTypeKind.GeometryLineString: + // break; + //case PrimitiveTypeKind.GeometryPolygon: + // break; + //case PrimitiveTypeKind.GeometryMultiPoint: + // break; + //case PrimitiveTypeKind.GeometryMultiLineString: + // break; + //case PrimitiveTypeKind.GeometryMultiPolygon: + // break; + //case PrimitiveTypeKind.GeometryCollection: + // break; + //case PrimitiveTypeKind.GeographyPoint: + // break; + //case PrimitiveTypeKind.GeographyLineString: + // break; + //case PrimitiveTypeKind.GeographyPolygon: + // break; + //case PrimitiveTypeKind.GeographyMultiPoint: + // break; + //case PrimitiveTypeKind.GeographyMultiLineString: + // break; + //case PrimitiveTypeKind.GeographyMultiPolygon: + // break; + //case PrimitiveTypeKind.GeographyCollection: + // break; + default: + throw new ArgumentException("Unhandled column type:" + column.Type); + } + } + + void AppendTableName(string tableName, StringBuilder sql) + { + var dotIndex = tableName.IndexOf('.'); if (dotIndex == -1) - { - sql.Append('"'); - sql.Append(tableName); - sql.Append('"'); - } + AppendQuotedIdentifier(tableName, sql); else { - sql.Append('"'); - sql.Append(tableName.Remove(dotIndex)); - sql.Append("\".\""); - sql.Append(tableName.Substring(dotIndex + 1)); - sql.Append('"'); + AppendQuotedIdentifier(tableName.Remove(dotIndex), sql); + sql.Append('.'); + AppendQuotedIdentifier(tableName.Substring(dotIndex + 1), sql); } } @@ -772,12 +706,10 @@ private void AppendTableName(string tableName, StringBuilder sql) #region Value appenders - private void AppendValue(byte[] values, StringBuilder sql) + void AppendValue(byte[] values, StringBuilder sql) { if (values.Length == 0) - { sql.Append("''"); - } else { sql.Append("E'\\\\"); @@ -787,91 +719,71 @@ private void AppendValue(byte[] values, StringBuilder sql) } } - private void AppendValue(bool value, StringBuilder sql) - { - sql.Append(value ? "TRUE" : "FALSE"); - } + void AppendValue(bool value, StringBuilder sql) + => sql.Append(value ? "TRUE" : "FALSE"); - private void AppendValue(DateTime value, StringBuilder sql) + void AppendValue(DateTime value, StringBuilder sql) { sql.Append("'"); sql.Append(new NpgsqlTypes.NpgsqlDateTime(value)); sql.Append("'"); } - private void AppendValue(DateTimeOffset value, StringBuilder sql) + void AppendValue(DateTimeOffset value, StringBuilder sql) { sql.Append("'"); sql.Append(new NpgsqlTypes.NpgsqlDateTime(value.UtcDateTime)); sql.Append("'"); } - private void AppendValue(Guid value, StringBuilder sql) + void AppendValue(Guid value, StringBuilder sql) { sql.Append("'"); sql.Append(value); sql.Append("'"); } - private void AppendValue(string value, StringBuilder sql) + void AppendValue(string value, StringBuilder sql) { sql.Append("'"); sql.Append(value); sql.Append("'"); } - private void AppendValue(TimeSpan value, StringBuilder sql) + void AppendValue(TimeSpan value, StringBuilder sql) { sql.Append("'"); - sql.Append(new NpgsqlTypes.NpgsqlTimeSpan(value).ToString()); + sql.Append(new NpgsqlTypes.NpgsqlTimeSpan(value)); sql.Append("'"); } - private void AppendValue(DbGeometry value, StringBuilder sql) + void AppendValue(DbGeometry value, StringBuilder sql) { sql.Append("'"); sql.Append(value); sql.Append("'"); } - private void AppendValue(object value, StringBuilder sql) - { - if (value is byte[]) - { - AppendValue((byte[])value, sql); - } - else if (value is bool) - { - AppendValue((bool)value, sql); - } - else if (value is DateTime) - { - AppendValue((DateTime)value, sql); - } - else if (value is DateTimeOffset) - { - AppendValue((DateTimeOffset)value, sql); - } - else if (value is Guid) - { - AppendValue((Guid)value, sql); - } - else if (value is string) - { - AppendValue((string)value, sql); - } - else if (value is TimeSpan) - { - AppendValue((TimeSpan)value, sql); - } - else if (value is DbGeometry) - { - AppendValue((DbGeometry)value, sql); - } + void AppendValue(object value, StringBuilder sql) + { + if (value is byte[] bytes) + AppendValue(bytes, sql); + else if (value is bool b) + AppendValue(b, sql); + else if (value is DateTime time) + AppendValue(time, sql); + else if (value is DateTimeOffset offset) + AppendValue(offset, sql); + else if (value is Guid guid) + AppendValue(guid, sql); + else if (value is string s) + AppendValue(s, sql); + else if (value is TimeSpan timeSpan) + AppendValue(timeSpan, sql); + else if (value is DbGeometry geometry) + AppendValue(geometry, sql); else - { sql.Append(string.Format(CultureInfo.InvariantCulture, "{0}", value)); - } } #endregion diff --git a/EF6.PG/NpgsqlProviderManifest.cs b/EF6.PG/NpgsqlProviderManifest.cs new file mode 100644 index 0000000..a76542a --- /dev/null +++ b/EF6.PG/NpgsqlProviderManifest.cs @@ -0,0 +1,382 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Data.Entity.Core.Common; +using System.Data.Entity.Core.Metadata.Edm; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Xml; +using JetBrains.Annotations; +using NpgsqlTypes; + +namespace Npgsql +{ + internal class NpgsqlProviderManifest : DbXmlEnabledProviderManifest + { + public Version Version { get; } + + public NpgsqlProviderManifest(string serverVersion) + : base(CreateXmlReaderForResource("Npgsql.Resources.NpgsqlProviderManifest.Manifest.xml")) + { + Version = Version.TryParse(serverVersion, out var version) + ? version + : new Version(9, 5); + } + + protected override XmlReader GetDbInformation([NotNull] string informationType) + { + if (informationType == StoreSchemaDefinition) + return CreateXmlReaderForResource("Npgsql.Resources.NpgsqlSchema.ssdl"); + if (informationType == StoreSchemaDefinitionVersion3) + return CreateXmlReaderForResource("Npgsql.Resources.NpgsqlSchemaV3.ssdl"); + if (informationType == StoreSchemaMapping) + return CreateXmlReaderForResource("Npgsql.Resources.NpgsqlSchema.msl"); + + throw new ArgumentOutOfRangeException(nameof(informationType)); + } + + const string MaxLengthFacet = "MaxLength"; + const string ScaleFacet = "Scale"; + const string PrecisionFacet = "Precision"; + const string FixedLengthFacet = "FixedLength"; + + internal static NpgsqlDbType GetNpgsqlDbType(PrimitiveTypeKind primitiveType) + { + switch (primitiveType) + { + case PrimitiveTypeKind.Binary: + return NpgsqlDbType.Bytea; + case PrimitiveTypeKind.Boolean: + return NpgsqlDbType.Boolean; + case PrimitiveTypeKind.Byte: + case PrimitiveTypeKind.SByte: + case PrimitiveTypeKind.Int16: + return NpgsqlDbType.Smallint; + case PrimitiveTypeKind.DateTime: + return NpgsqlDbType.Timestamp; + case PrimitiveTypeKind.DateTimeOffset: + return NpgsqlDbType.TimestampTz; + case PrimitiveTypeKind.Decimal: + return NpgsqlDbType.Numeric; + case PrimitiveTypeKind.Double: + return NpgsqlDbType.Double; + case PrimitiveTypeKind.Int32: + return NpgsqlDbType.Integer; + case PrimitiveTypeKind.Int64: + return NpgsqlDbType.Bigint; + case PrimitiveTypeKind.Single: + return NpgsqlDbType.Real; + case PrimitiveTypeKind.Time: + return NpgsqlDbType.Interval; + case PrimitiveTypeKind.Guid: + return NpgsqlDbType.Uuid; + case PrimitiveTypeKind.String: + // Send strings as unknowns to be compatible with other datatypes than text + return NpgsqlDbType.Unknown; + default: + return NpgsqlDbType.Unknown; + } + } + + public override TypeUsage GetEdmType([NotNull] TypeUsage storeType) + { + if (storeType == null) + throw new ArgumentNullException(nameof(storeType)); + + var storeTypeName = storeType.EdmType.Name; + var primitiveType = StoreTypeNameToEdmPrimitiveType[storeTypeName]; + // TODO: come up with way to determin if unicode is used + var isUnicode = true; + Facet facet; + + switch (storeTypeName) + { + case "bool": + case "int2": + case "int4": + case "int8": + case "float4": + case "float8": + case "uuid": + return TypeUsage.CreateDefaultTypeUsage(primitiveType); + case "numeric": + { + if (storeType.Facets.TryGetValue(ScaleFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + var scale = (byte)facet.Value; + if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + var precision = (byte)facet.Value; + return TypeUsage.CreateDecimalTypeUsage(primitiveType, precision, scale); + } + } + return TypeUsage.CreateDecimalTypeUsage(primitiveType); + } + case "bpchar": + if (storeType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, true, (int)facet.Value); + else + return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, true); + case "varchar": + if (storeType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, false, (int)facet.Value); + else + return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, false); + case "text": + case "xml": + return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, false); + case "timestamp": + // TODO: make sure the arguments are correct here + if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateDateTimeTypeUsage(primitiveType, (byte)facet.Value); + } + else + { + return TypeUsage.CreateDateTimeTypeUsage(primitiveType, null); + } + case "date": + return TypeUsage.CreateDateTimeTypeUsage(primitiveType, 0); + case "timestamptz": + if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateDateTimeOffsetTypeUsage(primitiveType, (byte)facet.Value); + } + else + { + return TypeUsage.CreateDateTimeOffsetTypeUsage(primitiveType, null); + } + case "time": + case "interval": + if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateTimeTypeUsage(primitiveType, (byte)facet.Value); + } + else + { + return TypeUsage.CreateTimeTypeUsage(primitiveType, null); + } + case "bytea": + { + if (storeType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateBinaryTypeUsage(primitiveType, false, (int)facet.Value); + } + return TypeUsage.CreateBinaryTypeUsage(primitiveType, false); + } + case "rowversion": + { + return TypeUsage.CreateBinaryTypeUsage(primitiveType, true, 8); + } + //TypeUsage.CreateBinaryTypeUsage + //TypeUsage.CreateDateTimeTypeUsage + //TypeUsage.CreateDecimalTypeUsage + //TypeUsage.CreateStringTypeUsage + } + + throw new NotSupportedException("Not supported store type: " + storeTypeName); + } + + public override TypeUsage GetStoreType([NotNull] TypeUsage edmType) + { + if (edmType == null) + throw new ArgumentNullException(nameof(edmType)); + + var primitiveType = edmType.EdmType as PrimitiveType; + if (primitiveType == null) + throw new ArgumentException("Store does not support specified edm type"); + + // TODO: come up with way to determin if unicode is used + var isUnicode = true; + Facet facet; + + switch (primitiveType.PrimitiveTypeKind) + { + case PrimitiveTypeKind.Boolean: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["bool"]); + case PrimitiveTypeKind.Int16: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int2"]); + case PrimitiveTypeKind.Int32: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int4"]); + case PrimitiveTypeKind.Int64: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int8"]); + case PrimitiveTypeKind.Single: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["float4"]); + case PrimitiveTypeKind.Double: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["float8"]); + case PrimitiveTypeKind.Decimal: + { + if (edmType.Facets.TryGetValue(ScaleFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + var scale = (byte)facet.Value; + if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + var precision = (byte)facet.Value; + return TypeUsage.CreateDecimalTypeUsage(StoreTypeNameToStorePrimitiveType["numeric"], precision, scale); + } + } + return TypeUsage.CreateDecimalTypeUsage(StoreTypeNameToStorePrimitiveType["numeric"]); + } + case PrimitiveTypeKind.String: + { + // TODO: could get character, character varying, text + if (edmType.Facets.TryGetValue(FixedLengthFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null && (bool)facet.Value) + { + PrimitiveType characterPrimitive = StoreTypeNameToStorePrimitiveType["bpchar"]; + if (edmType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateStringTypeUsage(characterPrimitive, isUnicode, true, (int)facet.Value); + } + // this may not work well + return TypeUsage.CreateStringTypeUsage(characterPrimitive, isUnicode, true); + } + if (edmType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["varchar"], isUnicode, false, (int)facet.Value); + } + // assume text since it is not fixed length and has no max length + return TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["text"], isUnicode, false); + } + case PrimitiveTypeKind.DateTime: + if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateDateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["timestamp"], (byte)facet.Value); + } + else + { + return TypeUsage.CreateDateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["timestamp"], null); + } + case PrimitiveTypeKind.DateTimeOffset: + if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateDateTimeOffsetTypeUsage(StoreTypeNameToStorePrimitiveType["timestamptz"], (byte)facet.Value); + } + else + { + return TypeUsage.CreateDateTimeOffsetTypeUsage(StoreTypeNameToStorePrimitiveType["timestamptz"], null); + } + case PrimitiveTypeKind.Time: + if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["interval"], (byte)facet.Value); + } + else + { + return TypeUsage.CreateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["interval"], null); + } + case PrimitiveTypeKind.Binary: + { + if (edmType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && + !facet.IsUnbounded && facet.Value != null) + { + return TypeUsage.CreateBinaryTypeUsage(StoreTypeNameToStorePrimitiveType["bytea"], false, (int)facet.Value); + } + return TypeUsage.CreateBinaryTypeUsage(StoreTypeNameToStorePrimitiveType["bytea"], false); + } + case PrimitiveTypeKind.Guid: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["uuid"]); + case PrimitiveTypeKind.Byte: + case PrimitiveTypeKind.SByte: + return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int2"]); + } + + throw new NotSupportedException("Not supported edm type: " + edmType); + } + + static XmlReader CreateXmlReaderForResource(string resourceName) + { + var stream = Assembly.GetAssembly(typeof(NpgsqlProviderManifest)).GetManifestResourceStream(resourceName); + if (stream == null) + throw new InvalidOperationException($"Could not find resource {resourceName} in assembly, please report issue"); + return XmlReader.Create(stream); + } + + public override bool SupportsEscapingLikeArgument(out char escapeCharacter) + { + escapeCharacter = '\\'; + return true; + } + + public override string EscapeLikeArgument([NotNull] string argument) + => argument.Replace("\\","\\\\").Replace("%", "\\%").Replace("_", "\\_"); + + public override bool SupportsInExpression() => true; + + public override ReadOnlyCollection GetStoreFunctions() + => new[] { typeof(NpgsqlTextFunctions).GetTypeInfo(), typeof(NpgsqlTypeFunctions) } + .SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.Static)) + .Select(x => new { Method = x, DbFunction = x.GetCustomAttribute() }) + .Where(x => x.DbFunction != null) + .Select(x => CreateComposableEdmFunction(x.Method, x.DbFunction)) + .ToList() + .AsReadOnly(); + + static EdmFunction CreateComposableEdmFunction([NotNull] MethodInfo method, [NotNull] DbFunctionAttribute dbFunctionInfo) + { + if (method == null) + throw new ArgumentNullException(nameof(method)); + if (dbFunctionInfo == null) + throw new ArgumentNullException(nameof(dbFunctionInfo)); + + return EdmFunction.Create( + dbFunctionInfo.FunctionName, + dbFunctionInfo.NamespaceName, + DataSpace.SSpace, + new EdmFunctionPayload + { + ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, + Schema = string.Empty, + IsBuiltIn = true, + IsAggregate = false, + IsFromProviderManifest = true, + StoreFunctionName = dbFunctionInfo.FunctionName, + IsComposable = true, + ReturnParameters = new[] + { + FunctionParameter.Create( + "ReturnType", + MapTypeToEdmType(method.ReturnType), + ParameterMode.ReturnValue) + }, + Parameters = method.GetParameters().Select( + x => FunctionParameter.Create( + x.Name, + MapTypeToEdmType(x.ParameterType), + ParameterMode.In)).ToList() + }, + new List()); + } + + static EdmType MapTypeToEdmType(Type type) + { + var fromClrType = PrimitiveType + .GetEdmPrimitiveTypes() + .FirstOrDefault(t => t.ClrEquivalentType == type); + + if (fromClrType != null) + return fromClrType; + + if (type.IsEnum) + return MapTypeToEdmType(Enum.GetUnderlyingType(type)); + + throw new NotSupportedException($"Unsupported type for mapping to EdmType: {type.FullName}"); + } + } +} diff --git a/src/EntityFramework6.Npgsql/NpgsqlRankingNormalization.cs b/EF6.PG/NpgsqlRankingNormalization.cs similarity index 100% rename from src/EntityFramework6.Npgsql/NpgsqlRankingNormalization.cs rename to EF6.PG/NpgsqlRankingNormalization.cs diff --git a/EF6.PG/NpgsqlServices.cs b/EF6.PG/NpgsqlServices.cs new file mode 100644 index 0000000..4108f76 --- /dev/null +++ b/EF6.PG/NpgsqlServices.cs @@ -0,0 +1,186 @@ +using System; +using System.Text; +using JetBrains.Annotations; +using System.Data.Entity.Core.Common; +using System.Data.Entity.Core.Common.CommandTrees; +using System.Data.Entity.Core.Metadata.Edm; +using System.Data.Entity.Migrations.Sql; +using System.Data.Entity.Infrastructure.DependencyResolution; +using Npgsql.SqlGenerators; +using DbConnection = System.Data.Common.DbConnection; +using DbCommand = System.Data.Common.DbCommand; +using System.Data.Common; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +namespace Npgsql +{ + [PublicAPI] + public class NpgsqlServices : DbProviderServices + { + public static NpgsqlServices Instance { get; } = new NpgsqlServices(); + + public NpgsqlServices() + { + AddDependencyResolver(new SingletonDependencyResolver>( + () => new NpgsqlMigrationSqlGenerator(), nameof(Npgsql))); + } + + protected override DbCommandDefinition CreateDbCommandDefinition([NotNull] DbProviderManifest providerManifest, [NotNull] DbCommandTree commandTree) + => CreateCommandDefinition(CreateDbCommand(((NpgsqlProviderManifest)providerManifest).Version, commandTree)); + + internal DbCommand CreateDbCommand(Version serverVersion, DbCommandTree commandTree) + { + if (commandTree == null) + throw new ArgumentNullException(nameof(commandTree)); + + var command = new NpgsqlCommand(); + + foreach (var parameter in commandTree.Parameters) + { + var dbParameter = new NpgsqlParameter + { + ParameterName = parameter.Key, + NpgsqlDbType = NpgsqlProviderManifest.GetNpgsqlDbType(((PrimitiveType)parameter.Value.EdmType).PrimitiveTypeKind) + }; + command.Parameters.Add(dbParameter); + } + + TranslateCommandTree(serverVersion, commandTree, command); + + return command; + } + + protected override void SetDbParameterValue(DbParameter parameter, TypeUsage parameterType, object value) + { + base.SetDbParameterValue(parameter, parameterType, value); + ConvertValueToNumericIfEnum(parameter); + } + + // Npgsql > 4.0 does strict type checks on integral values and fails with enums passed with numeric DbType. + static void ConvertValueToNumericIfEnum(DbParameter parameter) + { + if (parameter.Value == null) + { + return; + } + + var parameterValueObjectType = parameter.Value.GetType(); + + if (!parameterValueObjectType.IsEnum) + { + return; + } + + var underlyingType = Enum.GetUnderlyingType(parameterValueObjectType); + parameter.Value = Convert.ChangeType(parameter.Value, underlyingType); + } + + internal void TranslateCommandTree(Version serverVersion, DbCommandTree commandTree, DbCommand command, bool createParametersForNonSelect = true) + { + SqlBaseGenerator sqlGenerator; + + DbQueryCommandTree select; + DbInsertCommandTree insert; + DbUpdateCommandTree update; + DbDeleteCommandTree delete; + DbFunctionCommandTree function; + if ((select = commandTree as DbQueryCommandTree) != null) + sqlGenerator = new SqlSelectGenerator(select); + else if ((insert = commandTree as DbInsertCommandTree) != null) + sqlGenerator = new SqlInsertGenerator(insert); + else if ((update = commandTree as DbUpdateCommandTree) != null) + sqlGenerator = new SqlUpdateGenerator(update); + else if ((delete = commandTree as DbDeleteCommandTree) != null) + sqlGenerator = new SqlDeleteGenerator(delete); + else if ((function = commandTree as DbFunctionCommandTree) != null) + sqlGenerator = new SqlFunctionGenerator(function); + else + { + // TODO: get a message (unsupported DbCommandTree type) + throw new ArgumentException(); + } + sqlGenerator.CreateParametersForConstants = select == null && createParametersForNonSelect; + sqlGenerator.Command = (NpgsqlCommand)command; + sqlGenerator.Version = serverVersion; + + sqlGenerator.BuildCommand(command); + } + + protected override string GetDbProviderManifestToken([NotNull] DbConnection connection) + { + if (connection == null) + throw new ArgumentNullException(nameof(connection)); + + var serverVersion = ""; + UsingPostgresDbConnection((NpgsqlConnection)connection, conn => { + serverVersion = conn.ServerVersion; + }); + return serverVersion; + } + + protected override DbProviderManifest GetDbProviderManifest([NotNull] string versionHint) + { + if (versionHint == null) + throw new ArgumentNullException(nameof(versionHint)); + return new NpgsqlProviderManifest(versionHint); + } + + protected override bool DbDatabaseExists([NotNull] DbConnection connection, int? commandTimeout, [NotNull] StoreItemCollection storeItemCollection) + { + var exists = false; + UsingPostgresDbConnection((NpgsqlConnection)connection, conn => + { + using (var command = new NpgsqlCommand("select count(*) from pg_catalog.pg_database where datname = '" + connection.Database + "';", conn)) + exists = Convert.ToInt32(command.ExecuteScalar()) > 0; + }); + return exists; + } + + protected override void DbCreateDatabase([NotNull] DbConnection connection, int? commandTimeout, [NotNull] StoreItemCollection storeItemCollection) + { + UsingPostgresDbConnection((NpgsqlConnection)connection, conn => + { + var sb = new StringBuilder(); + 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(); + }); + } + + protected override void DbDeleteDatabase([NotNull] DbConnection connection, int? commandTimeout, [NotNull] StoreItemCollection storeItemCollection) + { + UsingPostgresDbConnection((NpgsqlConnection)connection, conn => + { + //Close all connections in pool or exception "database used by another user appears" + NpgsqlConnection.ClearAllPools(); + using (var command = new NpgsqlCommand("DROP DATABASE \"" + connection.Database + "\";", conn)) + command.ExecuteNonQuery(); + }); + } + + static void UsingPostgresDbConnection(NpgsqlConnection connection, Action action) + { + var connectionBuilder = new NpgsqlConnectionStringBuilder(connection.ConnectionString) + { + Database = connection.Settings.EntityAdminDatabase ?? "template1", + Pooling = false + }; + + using (var masterConnection = connection.CloneWith(connectionBuilder.ConnectionString)) + { + masterConnection.Open();//using's Dispose will close it even if exception... + action(masterConnection); + } + } + } +} diff --git a/src/EntityFramework6.Npgsql/NpgsqlTextFunctions.cs b/EF6.PG/NpgsqlTextFunctions.cs similarity index 72% rename from src/EntityFramework6.Npgsql/NpgsqlTextFunctions.cs rename to EF6.PG/NpgsqlTextFunctions.cs index d31a7a5..f3cd584 100644 --- a/src/EntityFramework6.Npgsql/NpgsqlTextFunctions.cs +++ b/EF6.PG/NpgsqlTextFunctions.cs @@ -1,6 +1,7 @@ using System; using System.Data.Entity; using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; namespace Npgsql { @@ -16,20 +17,14 @@ public static class NpgsqlTextFunctions /// Cast to the tsvector data type. /// [DbFunction("Npgsql", "as_tsvector")] - public static string AsTsVector(string vector) - { - throw new NotSupportedException(); - } + public static string AsTsVector(string vector) => throw new NotSupportedException(); /// /// Reduce to tsvector. /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-DOCUMENTS /// [DbFunction("Npgsql", "to_tsvector")] - public static string ToTsVector(string document) - { - throw new NotSupportedException(); - } + public static string ToTsVector(string document) => throw new NotSupportedException(); /// /// Reduce to tsvector using the text search configuration specified @@ -37,29 +32,20 @@ public static string ToTsVector(string document) /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-DOCUMENTS /// [DbFunction("Npgsql", "to_tsvector")] - public static string ToTsVector(string config, string document) - { - throw new NotSupportedException(); - } + public static string ToTsVector(string config, string document) => throw new NotSupportedException(); /// /// Cast to the tsquery data type. /// [DbFunction("Npgsql", "as_tsquery")] - public static string AsTsQuery(string query) - { - throw new NotSupportedException(); - } + public static string AsTsQuery(string query) => throw new NotSupportedException(); /// /// Produce tsquery from ignoring punctuation. /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES /// [DbFunction("Npgsql", "plainto_tsquery")] - public static string PlainToTsQuery(string query) - { - throw new NotSupportedException(); - } + public static string PlainToTsQuery(string query) => throw new NotSupportedException(); /// /// Produce tsquery from ignoring punctuation and using the text search @@ -67,10 +53,7 @@ public static string PlainToTsQuery(string query) /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES /// [DbFunction("Npgsql", "plainto_tsquery")] - public static string PlainToTsQuery(string config, string query) - { - throw new NotSupportedException(); - } + public static string PlainToTsQuery(string config, string query) => throw new NotSupportedException(); /// /// Normalize words in and convert to tsquery. If your input @@ -79,10 +62,7 @@ public static string PlainToTsQuery(string config, string query) /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES /// [DbFunction("Npgsql", "to_tsquery")] - public static string ToTsQuery(string query) - { - throw new NotSupportedException(); - } + public static string ToTsQuery(string query) => throw new NotSupportedException(); /// /// Normalize words in and convert to tsquery using the text search @@ -92,40 +72,28 @@ public static string ToTsQuery(string query) /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES /// [DbFunction("Npgsql", "to_tsquery")] - public static string ToTsQuery(string config, string query) - { - throw new NotSupportedException(); - } + public static string ToTsQuery(string config, string query) => throw new NotSupportedException(); /// /// AND tsquerys together. Generates the "&&" operator. /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY /// [DbFunction("Npgsql", "operator_tsquery_and")] - public static string QueryAnd(string tsquery1, string tsquery2) - { - throw new NotSupportedException(); - } + public static string QueryAnd(string tsquery1, string tsquery2) => throw new NotSupportedException(); /// /// OR tsquerys together. Generates the "||" operator. /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY /// [DbFunction("Npgsql", "operator_tsquery_or")] - public static string QueryOr(string tsquery1, string tsquery2) - { - throw new NotSupportedException(); - } + public static string QueryOr(string tsquery1, string tsquery2) => throw new NotSupportedException(); /// /// Negate a tsquery. Generates the "!!" operator. /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY /// [DbFunction("Npgsql", "operator_tsquery_negate")] - public static string QueryNot(string tsquery) - { - throw new NotSupportedException(); - } + public static string QueryNot(string tsquery) => throw new NotSupportedException(); /// /// Returns whether contains . @@ -133,10 +101,7 @@ public static string QueryNot(string tsquery) /// http://www.postgresql.org/docs/current/static/functions-textsearch.html /// [DbFunction("Npgsql", "operator_tsquery_contains")] - public static bool QueryContains(string tsquery1, string tsquery2) - { - throw new NotSupportedException(); - } + public static bool QueryContains(string tsquery1, string tsquery2) => throw new NotSupportedException(); /// /// Returns whether is contained within . @@ -144,20 +109,14 @@ public static bool QueryContains(string tsquery1, string tsquery2) /// http://www.postgresql.org/docs/current/static/functions-textsearch.html /// [DbFunction("Npgsql", "operator_tsquery_is_contained")] - public static bool QueryIsContained(string tsquery1, string tsquery2) - { - throw new NotSupportedException(); - } + public static bool QueryIsContained(string tsquery1, string tsquery2) => throw new NotSupportedException(); /// /// This method generates the "@@" match operator. /// http://www.postgresql.org/docs/current/static/textsearch-intro.html#TEXTSEARCH-MATCHING /// [DbFunction("Npgsql", "@@")] - public static bool Match(string tsvector, string tsquery) - { - throw new NotSupportedException(); - } + public static bool Match(string tsvector, string tsquery) => throw new NotSupportedException(); /// /// Assign weight to each element of and return a new @@ -165,30 +124,21 @@ public static bool Match(string tsvector, string tsquery) /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSVECTOR /// [DbFunction("Npgsql", "setweight")] - public static string SetWeight(string tsvector, NpgsqlWeightLabel label) - { - throw new NotSupportedException(); - } + public static string SetWeight(string tsvector, NpgsqlWeightLabel label) => throw new NotSupportedException(); /// /// Returns the number of lexemes in . /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSVECTOR /// [DbFunction("Npgsql", "length")] - public static int Length(string tsvector) - { - throw new NotSupportedException(); - } + public static int Length(string tsvector) => throw new NotSupportedException(); /// /// Returns the number of lexemes plus operators in . /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY /// [DbFunction("Npgsql", "numnode")] - public static int NumNode(string tsquery) - { - throw new NotSupportedException(); - } + public static int NumNode(string tsquery) => throw new NotSupportedException(); /// /// Removes weights and positions from and returns @@ -196,20 +146,14 @@ public static int NumNode(string tsquery) /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSVECTOR /// [DbFunction("Npgsql", "strip")] - public static string Strip(string tsvector) - { - throw new NotSupportedException(); - } + public static string Strip(string tsvector) => throw new NotSupportedException(); /// /// Get indexable part of . /// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY /// [DbFunction("Npgsql", "querytree")] - public static string QueryTree(string query) - { - throw new NotSupportedException(); - } + public static string QueryTree(string query) => throw new NotSupportedException(); /// /// Returns a string suitable for display containing a query match. @@ -217,9 +161,7 @@ public static string QueryTree(string query) /// [DbFunction("Npgsql", "ts_headline")] public static string TsHeadline(string document, string tsquery, string options) - { - throw new NotSupportedException(); - } + => throw new NotSupportedException(); /// /// Returns a string suitable for display containing a query match using the text @@ -228,21 +170,14 @@ public static string TsHeadline(string document, string tsquery, string options) /// [DbFunction("Npgsql", "ts_headline")] public static string TsHeadline(string config, string document, string tsquery, string options) - { - throw new NotSupportedException(); - } + => throw new NotSupportedException(); /// /// Calculates the rank of for . /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-RANKING /// [DbFunction("Npgsql", "ts_rank")] - public static float TsRank( - string vector, - string query) - { - throw new NotSupportedException(); - } + public static float TsRank(string vector, string query) => throw new NotSupportedException(); /// /// Calculates the rank of for while normalizing @@ -250,13 +185,8 @@ public static float TsRank( /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-RANKING /// [DbFunction("Npgsql", "ts_rank")] - public static float TsRank( - string vector, - string query, - NpgsqlRankingNormalization normalization) - { - throw new NotSupportedException(); - } + public static float TsRank(string vector, string query, NpgsqlRankingNormalization normalization) + => throw new NotSupportedException(); /// /// Calculates the rank of for with custom @@ -271,9 +201,7 @@ public static float TsRank( float weightA, string vector, string query) - { - throw new NotSupportedException(); - } + => throw new NotSupportedException(); /// /// Calculates the rank of for while normalizing @@ -290,9 +218,7 @@ public static float TsRank( string vector, string query, NpgsqlRankingNormalization normalization) - { - throw new NotSupportedException(); - } + => throw new NotSupportedException(); /// /// Calculates the rank of for using the cover @@ -300,12 +226,7 @@ public static float TsRank( /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-RANKING /// [DbFunction("Npgsql", "ts_rank_cd")] - public static float TsRankCd( - string vector, - string query) - { - throw new NotSupportedException(); - } + public static float TsRankCd(string vector, string query) => throw new NotSupportedException(); /// /// Calculates the rank of for using the cover @@ -314,13 +235,8 @@ public static float TsRankCd( /// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-RANKING /// [DbFunction("Npgsql", "ts_rank_cd")] - public static float TsRankCd( - string vector, - string query, - NpgsqlRankingNormalization normalization) - { - throw new NotSupportedException(); - } + public static float TsRankCd(string vector, string query, NpgsqlRankingNormalization normalization) + => throw new NotSupportedException(); /// /// Calculates the rank of for using the cover @@ -335,9 +251,7 @@ public static float TsRankCd( float weightA, string vector, string query) - { - throw new NotSupportedException(); - } + => throw new NotSupportedException(); /// /// Calculates the rank of for using the cover density @@ -354,9 +268,7 @@ public static float TsRankCd( string vector, string query, NpgsqlRankingNormalization normalization) - { - throw new NotSupportedException(); - } + => throw new NotSupportedException(); /// /// Searchs for occurrences of , and replaces @@ -365,8 +277,47 @@ public static float TsRankCd( /// [DbFunction("Npgsql", "ts_rewrite")] public static string TsRewrite(string query, string target, string substitute) - { - throw new NotSupportedException(); - } - } + => throw new NotSupportedException(); + + /// + /// Returns a tsquery that searches for a match to followed by a match + /// to . + /// https://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY + /// + [DbFunction("Npgsql", "tsquery_phrase")] + public static string TsQueryPhrase(string query1, string query2) + => throw new NotSupportedException(); + + /// + /// Returns a tsquery that searches for a match to followed by a match + /// to at a distance of lexemes using + /// the <N> tsquery operator + /// https://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY + /// + [DbFunction("Npgsql", "tsquery_phrase")] + public static string TsQueryPhrase(string query1, string query2, int distance) + => throw new NotSupportedException(); + + /// + /// Matches regular expression. Generates the "~" operator. + /// http://www.postgresql.org/docs/current/static/functions-matching.html#FUNCTIONS-POSIX-REGEXP + /// This method follows the semantics of + /// and it is translated to the equivalent PostgreSQL expression when executed. + /// + [DbFunction("Npgsql", "match_regex")] + public static bool MatchRegex(string input, string pattern) + => throw new NotSupportedException(); + + /// + /// Matches regular expression. Generates the "~" operator. + /// http://www.postgresql.org/docs/current/static/functions-matching.html#FUNCTIONS-POSIX-REGEXP + /// This method follows the semantics of + /// and it is translated to the equivalent PostgreSQL expression when executed. + /// Options and + /// are not supported. + /// + [DbFunction("Npgsql", "match_regex")] + public static bool MatchRegex(string input, string pattern, RegexOptions options) + => throw new NotSupportedException(); + } } diff --git a/EF6.PG/NpgsqlTypeFunctions.cs b/EF6.PG/NpgsqlTypeFunctions.cs new file mode 100644 index 0000000..a469d32 --- /dev/null +++ b/EF6.PG/NpgsqlTypeFunctions.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 type manipulation SQL fragments. + /// + [SuppressMessage("ReSharper", "UnusedParameter.Global")] + public static class NpgsqlTypeFunctions + { + /// + /// Emits an explicit cast for unknown types sent as strings to their correct postgresql type. + /// + [DbFunction("Npgsql", "cast")] + public static string Cast(string unknownTypeValue, string postgresTypeName) + => throw new NotSupportedException(); + } +} diff --git a/src/EntityFramework6.Npgsql/NpgsqlWeightLabel.cs b/EF6.PG/NpgsqlWeightLabel.cs similarity index 100% rename from src/EntityFramework6.Npgsql/NpgsqlWeightLabel.cs rename to EF6.PG/NpgsqlWeightLabel.cs diff --git a/EF6.PG/Properties/AssemblyInfo.cs b/EF6.PG/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2caf9c9 --- /dev/null +++ b/EF6.PG/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.CompilerServices; +using System.Security; +using System.Reflection; +using System.Resources; + +// Additional assembly attributes are defined in GlobalAssemblyInfo.cs +[assembly: CLSCompliant(false)] +[assembly: AllowPartiallyTrustedCallers()] +[assembly: SecurityRules(SecurityRuleSet.Level1)] +[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.MainAssembly)] diff --git a/src/EntityFramework6.Npgsql/NpgsqlProviderManifest.Manifest.xml b/EF6.PG/Resources/NpgsqlProviderManifest.Manifest.xml similarity index 100% rename from src/EntityFramework6.Npgsql/NpgsqlProviderManifest.Manifest.xml rename to EF6.PG/Resources/NpgsqlProviderManifest.Manifest.xml diff --git a/src/EntityFramework6.Npgsql/NpgsqlSchema.msl b/EF6.PG/Resources/NpgsqlSchema.msl similarity index 100% rename from src/EntityFramework6.Npgsql/NpgsqlSchema.msl rename to EF6.PG/Resources/NpgsqlSchema.msl diff --git a/src/EntityFramework6.Npgsql/NpgsqlSchema.ssdl b/EF6.PG/Resources/NpgsqlSchema.ssdl similarity index 99% rename from src/EntityFramework6.Npgsql/NpgsqlSchema.ssdl rename to EF6.PG/Resources/NpgsqlSchema.ssdl index 50df816..d40b9d4 100644 --- a/src/EntityFramework6.Npgsql/NpgsqlSchema.ssdl +++ b/EF6.PG/Resources/NpgsqlSchema.ssdl @@ -57,7 +57,7 @@ end as is_identity, false as is_generated, -- default value column - ad.adsrc as default_value + pg_get_expr(ad.adbin, ad.adrelid) as default_value from pg_attribute a join pg_class c on a.attrelid = c.oid @@ -78,7 +78,7 @@ c.condeferrable as is_deferrable, c.condeferred as is_initially_deferred, c.contype as constraint_type, - c.consrc as expression, + pg_get_expr(c.conbin, c.conrelid) as expression, case c.confupdtype when 'c' then 'CASCADE' when 'n' then 'SET NULL' @@ -178,7 +178,7 @@ end as is_identity, false as is_generated, -- default value column - ad.adsrc as default_value + pg_get_expr(ad.adbin, ad.adrelid) as default_value from pg_attribute a join pg_class c on a.attrelid = c.oid diff --git a/src/EntityFramework6.Npgsql/NpgsqlSchemaV3.ssdl b/EF6.PG/Resources/NpgsqlSchemaV3.ssdl similarity index 99% rename from src/EntityFramework6.Npgsql/NpgsqlSchemaV3.ssdl rename to EF6.PG/Resources/NpgsqlSchemaV3.ssdl index dd04ebd..5411c61 100644 --- a/src/EntityFramework6.Npgsql/NpgsqlSchemaV3.ssdl +++ b/EF6.PG/Resources/NpgsqlSchemaV3.ssdl @@ -57,7 +57,7 @@ end as is_identity, false as is_generated, -- default value column - ad.adsrc as default_value + pg_get_expr(ad.adbin, ad.adrelid) as default_value from pg_attribute a join pg_class c on a.attrelid = c.oid @@ -78,7 +78,7 @@ c.condeferrable as is_deferrable, c.condeferred as is_initially_deferred, c.contype as constraint_type, - c.consrc as expression, + pg_get_expr(c.conbin, c.conrelid) as expression, case c.confupdtype when 'c' then 'CASCADE' when 'n' then 'SET NULL' @@ -178,7 +178,7 @@ end as is_identity, false as is_generated, -- default value column - ad.adsrc as default_value + pg_get_expr(ad.adbin, ad.adrelid) as default_value from pg_attribute a join pg_class c on a.attrelid = c.oid diff --git a/src/EntityFramework6.Npgsql/Spatial/PostgisDataReader.cs b/EF6.PG/Spatial/PostgisDataReader.cs similarity index 52% rename from src/EntityFramework6.Npgsql/Spatial/PostgisDataReader.cs rename to EF6.PG/Spatial/PostgisDataReader.cs index 63a9c53..60e901d 100644 --- a/src/EntityFramework6.Npgsql/Spatial/PostgisDataReader.cs +++ b/EF6.PG/Spatial/PostgisDataReader.cs @@ -1,22 +1,16 @@ using System; using System.Data; - -#if ENTITIES6 using System.Data.Entity.Spatial; -#else -using System.Data.Spatial; -#endif namespace Npgsql.Spatial { /// /// A postgis geometry service API. /// - public class PostgisDataReader - : DbSpatialDataReader, System.Data.IDataReader + public class PostgisDataReader : DbSpatialDataReader, IDataReader { - private PostgisServices _svcs; - private NpgsqlDataReader _rdr; + PostgisServices _svcs; + NpgsqlDataReader _rdr; /// /// Creates a new instance of postgis data reader using a specific instance of PostgisService. @@ -24,7 +18,6 @@ public class PostgisDataReader /// The service provider that DbGeometry instances will use. /// The underlying data reader. public PostgisDataReader(PostgisServices svcs, NpgsqlDataReader rdr) - :base() { _svcs = svcs; _rdr = rdr; @@ -34,347 +27,177 @@ public PostgisDataReader(PostgisServices svcs, NpgsqlDataReader rdr) /// Creates a new instance of postgis data reader. /// /// The underlying data reader. - public PostgisDataReader(NpgsqlDataReader rdr) - :this(new PostgisServices(),rdr) - { - - } + 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(); - } + => throw new NotImplementedException(); /// /// Get the DbGeometry value of a column, given its zero-based ordinal. /// - /// - /// public override DbGeometry GetGeometry(int ordinal) - { - return _svcs.GeometryFromProviderValue(_rdr[ordinal]); - } + => throw new NotImplementedException(); /// /// Get the value indicating wether a column is a Geometry value, given its zero-based ordinal. /// - /// - /// -#if ENTITIES6 public override bool IsGeometryColumn(int ordinal) -#else - public bool IsGeometryColumn(int ordinal) -#endif - { - return _rdr[ordinal] is NpgsqlTypes.PostgisGeometry; - } + => throw new NotImplementedException(); /// /// Get the value indicating wether a column is a Geography value, given its zero-based ordinal. /// - /// - /// -#if ENTITIES6 public override bool IsGeographyColumn(int ordinal) -#else - public bool IsGeographyColumn(int ordinal) -#endif - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); /// /// Gets a value indicating the depth of nesting of the current row. Always Zero. /// - public int Depth - { - get - { - return _rdr.Depth; - } - } + public int Depth => _rdr.Depth; /// /// Gets a value indicating wether the data reader is closed. /// - public bool IsClosed - { - get - { - return _rdr.IsClosed; - } - } + public bool IsClosed => _rdr.IsClosed; /// /// Gets the number of row affected by the SQL statement. /// - public int RecordsAffected - { - get - { - return _rdr.RecordsAffected; - } - } + public int RecordsAffected => _rdr.RecordsAffected; /// /// Gets the number of columns in the current row. /// - public int FieldCount - { - get - { - return _rdr.FieldCount; - } - } + public int FieldCount => _rdr.FieldCount; /// /// Gets the value of the specified column name of the current row. /// - /// - /// - public object this[string name] - { - get - { - return _rdr[name]; - } - } + public object this[string name] => _rdr[name]; /// /// Gets the value of the specified column index of the current row. /// - /// - /// - public object this[int i] - { - get - { - return _rdr[i]; - } - } + public object this[int i] => _rdr[i]; /// /// Close the underlying datareader object. /// - public void Close() - { - _rdr.Close(); - } + public void Close() => _rdr.Close(); /// /// Returns a DataTable which contains metadata about the current row. /// - /// - public DataTable GetSchemaTable() - { - return _rdr.GetSchemaTable(); - } + public DataTable GetSchemaTable() => _rdr.GetSchemaTable(); /// /// Advances the reader to the next result when reading data from a batch of statements. /// /// - public bool NextResult() - { - return _rdr.NextResult(); - } + public bool NextResult() => _rdr.NextResult(); /// /// Advances the reader to the next record in a result set. /// /// - public bool Read() - { - return _rdr.Read(); - } + public bool Read() => _rdr.Read(); /// /// Frees the resources hold by the data reader. /// - public void Dispose() - { - _rdr.Dispose(); - } + public void Dispose() => _rdr.Dispose(); /// /// Gets the name of a column, given a zero based ordinal. /// - /// - /// - public string GetName(int i) - { - return _rdr.GetName(i); - } + 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) - { - return _rdr.GetDataTypeName(i); - } - + 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) - { - return _rdr.GetFieldType(i); - } - + public Type GetFieldType(int i) => _rdr.GetFieldType(i); /// /// Gets the value of a column, given a zero based ordinal. /// - /// - /// - public object GetValue(int i) - { - return _rdr.GetValue(i); - } + 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) - { - return _rdr.GetValues(values); - } + public int GetValues(object[] values) => _rdr.GetValues(values); /// /// Gets the column ordinal given the column name. /// - /// - /// - public int GetOrdinal(string name) - { - return _rdr.GetOrdinal(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) - { - return _rdr.GetBoolean(i); - } + 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) - { - return _rdr.GetByte(i); - } + 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) - { - return _rdr.GetBytes(i, fieldOffset, buffer, bufferoffset, 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) - { - return _rdr.GetChar(i); - } + public char GetChar(int i) => _rdr.GetChar(i); public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) - { - return _rdr.GetChars(i, fieldoffset, buffer, bufferoffset, 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) - { - return _rdr.GetGuid(i); - } + 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) - { - return _rdr.GetInt16(i); - } + 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) - { - return _rdr.GetInt32(i); - } + 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) - { - return _rdr.GetInt64(i); - } + 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) - { - return _rdr.GetFloat(i); - } + 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) - { - return _rdr.GetDouble(i); - } + 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); @@ -383,8 +206,6 @@ public string GetString(int i) /// /// Get the value of a column as a decimal, given its zero-based ordinal. /// - /// - /// public decimal GetDecimal(int i) { return _rdr.GetDecimal(i); @@ -393,8 +214,6 @@ public decimal GetDecimal(int i) /// /// Get the value of a column as a datetime, given its zero-based ordinal. /// - /// - /// public DateTime GetDateTime(int i) { return _rdr.GetDateTime(i); @@ -403,8 +222,6 @@ public DateTime GetDateTime(int i) /// /// Returns a DbDataReader of a column, given its zero-based ordinal. /// - /// - /// public IDataReader GetData(int i) { return _rdr.GetData(i); @@ -413,8 +230,6 @@ public IDataReader GetData(int 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 new file mode 100644 index 0000000..227625c --- /dev/null +++ b/EF6.PG/Spatial/PostgisServices.cs @@ -0,0 +1,1283 @@ +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/PendingProjectsNode.cs b/EF6.PG/SqlGenerators/PendingProjectsNode.cs new file mode 100644 index 0000000..346a1e6 --- /dev/null +++ b/EF6.PG/SqlGenerators/PendingProjectsNode.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; + +namespace Npgsql.SqlGenerators +{ + /// + /// Represents an InputExpression and what alias it will have when used in a FROM clause + /// + internal class NameAndInputExpression + { + public string AsName { get; set; } + public InputExpression Exp { get; set; } + + public NameAndInputExpression(string asName, InputExpression exp) + { + AsName = asName; + Exp = exp; + } + } + + /// + /// A tree of subqueries, used when evaluating SQL text for DbPropertyExpressions in SqlSelectGenerator. + /// See SqlSelectGenerator.Visit(DbPropertyExpression) for more information. + /// + internal class PendingProjectsNode + { + public readonly List Selects = new List(); + public PendingProjectsNode JoinParent { get; set; } + public string TopName => Selects[0].AsName; + + public PendingProjectsNode(string asName, InputExpression exp) + { + Selects.Add(new NameAndInputExpression(asName, exp)); + } + + public void Add(string asName, InputExpression exp) + { + Selects.Add(new NameAndInputExpression(asName, exp)); + } + + public NameAndInputExpression Last => Selects[Selects.Count - 1]; + } +} diff --git a/EF6.PG/SqlGenerators/SqlBaseGenerator.cs b/EF6.PG/SqlGenerators/SqlBaseGenerator.cs new file mode 100644 index 0000000..b36413a --- /dev/null +++ b/EF6.PG/SqlGenerators/SqlBaseGenerator.cs @@ -0,0 +1,1460 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Diagnostics; +using System.Globalization; +using System.Data.Entity.Core.Common.CommandTrees; +using System.Data.Entity.Core.Metadata.Edm; +using System.Linq; +using JetBrains.Annotations; +using System.Text.RegularExpressions; +using System.Text; + +namespace Npgsql.SqlGenerators +{ + internal abstract class SqlBaseGenerator : DbExpressionVisitor + { + internal NpgsqlCommand Command; + internal bool CreateParametersForConstants; + bool _useNewPrecedences; + + protected Dictionary RefToNode = new Dictionary(); + protected HashSet CurrentExpressions = new HashSet(); + protected uint AliasCounter; + protected uint ParameterCount; + + internal Version Version + { + get { return _version; } + set + { + _version = value; + _useNewPrecedences = value >= new Version(9, 5); + } + } + Version _version; + + static readonly Dictionary AggregateFunctionNames = new Dictionary() + { + {"Avg","avg"}, + {"Count","count"}, + {"Min","min"}, + {"Max","max"}, + {"Sum","sum"}, + {"BigCount","count"}, + {"StDev","stddev_samp"}, + {"StDevP","stddev_pop"}, + {"Var","var_samp"}, + {"VarP","var_pop"}, + }; + + static readonly Dictionary BinaryOperatorFunctionNames = new Dictionary() + { + {"@@",Operator.QueryMatch}, + {"operator_tsquery_and",Operator.QueryAnd}, + {"operator_tsquery_or",Operator.QueryOr}, + {"operator_tsquery_contains",Operator.QueryContains}, + {"operator_tsquery_is_contained",Operator.QueryIsContained} + }; + + void EnterExpression(PendingProjectsNode n) => CurrentExpressions.Add(n.Last.Exp); + void LeaveExpression(PendingProjectsNode n) => CurrentExpressions.Remove(n.Last.Exp); + + protected string NextAlias() => "Alias" + AliasCounter++; + + bool IsCompatible(InputExpression child, DbExpressionKind parentKind) + { + switch (parentKind) + { + case DbExpressionKind.Filter: + return + child.Projection == null && + child.GroupBy == null && + child.Skip == null && + child.Limit == null; + case DbExpressionKind.GroupBy: + return + child.Projection == null && + child.GroupBy == null && + child.Distinct == false && + child.OrderBy == null && + child.Skip == null && + child.Limit == null; + case DbExpressionKind.Distinct: + return + child.OrderBy == null && + child.Skip == null && + child.Limit == null; + case DbExpressionKind.Sort: + return + child.Projection == null && + child.GroupBy == null && + child.Skip == null && + child.Limit == null; + case DbExpressionKind.Skip: + return + child.Projection == null && + child.Skip == null && + child.Limit == null; + case DbExpressionKind.Project: + return + child.Projection == null && + child.Distinct == false; + // Limit and NewInstance are always true + default: + throw new ArgumentException("Unexpected parent expression kind"); + } + } + + PendingProjectsNode GetInput(DbExpression expression, string childBindingName, string parentBindingName, DbExpressionKind parentKind) + { + var n = VisitInputWithBinding(expression, childBindingName); + if (!IsCompatible(n.Last.Exp, parentKind)) + n.Selects.Add(new NameAndInputExpression(parentBindingName, new InputExpression(n.Last.Exp, n.Last.AsName))); + return n; + } + + PendingProjectsNode VisitInputWithBinding(DbExpression expression, string bindingName) + { + PendingProjectsNode n; + switch (expression.ExpressionKind) + { + case DbExpressionKind.Scan: + { + var scan = (ScanExpression)expression.Accept(this); + var input = new InputExpression(scan, bindingName); + n = new PendingProjectsNode(bindingName, input); + + break; + } + case DbExpressionKind.Filter: + { + var exp = (DbFilterExpression)expression; + n = GetInput(exp.Input.Expression, exp.Input.VariableName, bindingName, expression.ExpressionKind); + EnterExpression(n); + var pred = exp.Predicate.Accept(this); + if (n.Last.Exp.Where == null) + n.Last.Exp.Where = new WhereExpression(pred); + else + n.Last.Exp.Where.And(pred); + LeaveExpression(n); + + break; + } + case DbExpressionKind.Sort: + { + var exp = (DbSortExpression)expression; + n = GetInput(exp.Input.Expression, exp.Input.VariableName, bindingName, expression.ExpressionKind); + EnterExpression(n); + n.Last.Exp.OrderBy = new OrderByExpression(); + foreach (var order in exp.SortOrder) + n.Last.Exp.OrderBy.AppendSort(order.Expression.Accept(this), order.Ascending); + LeaveExpression(n); + + break; + } + case DbExpressionKind.Skip: + { + var exp = (DbSkipExpression)expression; + n = GetInput(exp.Input.Expression, exp.Input.VariableName, bindingName, expression.ExpressionKind); + EnterExpression(n); + n.Last.Exp.OrderBy = new OrderByExpression(); + foreach (var order in exp.SortOrder) + n.Last.Exp.OrderBy.AppendSort(order.Expression.Accept(this), order.Ascending); + n.Last.Exp.Skip = new SkipExpression(exp.Count.Accept(this)); + LeaveExpression(n); + break; + } + case DbExpressionKind.Distinct: + { + var exp = (DbDistinctExpression)expression; + var childBindingName = NextAlias(); + + n = VisitInputWithBinding(exp.Argument, childBindingName); + if (!IsCompatible(n.Last.Exp, expression.ExpressionKind)) + { + var prev = n.Last.Exp; + var prevName = n.Last.AsName; + var input = new InputExpression(prev, prevName); + n.Selects.Add(new NameAndInputExpression(bindingName, input)); + + // We need to copy all the projected columns so the DISTINCT keyword will work on the correct columns + // A parent project expression is never compatible with this new expression, + // so these are the columns that finally will be projected, as wanted + foreach (ColumnExpression col in prev.Projection.Arguments) + { + input.ColumnsToProject.Add(new StringPair(prevName, col.Name), col.Name); + input.ProjectNewNames.Add(col.Name); + } + } + n.Last.Exp.Distinct = true; + break; + } + case DbExpressionKind.Limit: + { + var exp = (DbLimitExpression)expression; + n = VisitInputWithBinding(exp.Argument, NextAlias()); + if (n.Last.Exp.Limit != null) + { + var least = new FunctionExpression("LEAST"); + least.AddArgument(n.Last.Exp.Limit.Arg); + least.AddArgument(exp.Limit.Accept(this)); + n.Last.Exp.Limit.Arg = least; + } + else + n.Last.Exp.Limit = new LimitExpression(exp.Limit.Accept(this)); + break; + } + case DbExpressionKind.NewInstance: + { + var exp = (DbNewInstanceExpression)expression; + if (exp.Arguments.Count == 1 && exp.Arguments[0].ExpressionKind == DbExpressionKind.Element) + { + n = VisitInputWithBinding(((DbElementExpression)exp.Arguments[0]).Argument, NextAlias()); + if (n.Last.Exp.Limit != null) + { + var least = new FunctionExpression("LEAST"); + least.AddArgument(n.Last.Exp.Limit.Arg); + least.AddArgument(new LiteralExpression("1")); + n.Last.Exp.Limit.Arg = least; + } + else + n.Last.Exp.Limit = new LimitExpression(new LiteralExpression("1")); + } + else if (exp.Arguments.Count >= 1) + { + var result = new LiteralExpression("("); + for (var i = 0; i < exp.Arguments.Count; ++i) + { + var arg = exp.Arguments[i]; + var visitedColumn = arg.Accept(this); + if (!(visitedColumn is ColumnExpression)) + visitedColumn = new ColumnExpression(visitedColumn, "C", arg.ResultType); + + result.Append(i == 0 ? "SELECT " : " UNION ALL SELECT "); + result.Append(visitedColumn); + } + result.Append(")"); + n = new PendingProjectsNode(bindingName, new InputExpression(result, bindingName)); + } + else + { + var type = ((CollectionType)exp.ResultType.EdmType).TypeUsage; + var result = new LiteralExpression("(SELECT "); + result.Append(new CastExpression(new LiteralExpression("NULL"), GetDbType(type.EdmType))); + result.Append(" LIMIT 0)"); + n = new PendingProjectsNode(bindingName, new InputExpression(result, bindingName)); + } + break; + } + case DbExpressionKind.UnionAll: + case DbExpressionKind.Intersect: + case DbExpressionKind.Except: + { + var exp = (DbBinaryExpression)expression; + var expKind = exp.ExpressionKind; + var list = new List(); + Action func = null; + func = e => + { + if (e.ExpressionKind == expKind && e.ExpressionKind != DbExpressionKind.Except) + { + var binaryExp = (DbBinaryExpression)e; + func(binaryExp.Left); + func(binaryExp.Right); + } + else + list.Add(VisitInputWithBinding(e, bindingName + "_" + list.Count).Last.Exp); + }; + func(exp.Left); + func(exp.Right); + var input = new InputExpression(new CombinedProjectionExpression(expression.ExpressionKind, list), bindingName); + n = new PendingProjectsNode(bindingName, input); + break; + } + case DbExpressionKind.Project: + { + var exp = (DbProjectExpression)expression; + var child = VisitInputWithBinding(exp.Input.Expression, exp.Input.VariableName); + var input = child.Last.Exp; + var enterScope = false; + if (!IsCompatible(input, expression.ExpressionKind)) + input = new InputExpression(input, child.Last.AsName); + else + enterScope = true; + + if (enterScope) EnterExpression(child); + + input.Projection = new CommaSeparatedExpression(); + + var projection = (DbNewInstanceExpression)exp.Projection; + var rowType = (RowType)projection.ResultType.EdmType; + for (var i = 0; i < rowType.Properties.Count && i < projection.Arguments.Count; ++i) + { + var prop = rowType.Properties[i]; + var argument = projection.Arguments[i].Accept(this); + var constantArgument = projection.Arguments[i] as DbConstantExpression; + if (constantArgument != null && constantArgument.Value is string) + { + argument = new CastExpression(argument, "varchar"); + } + + input.Projection.Arguments.Add(new ColumnExpression(argument, prop.Name, prop.TypeUsage)); + } + + if (enterScope) LeaveExpression(child); + + n = new PendingProjectsNode(bindingName, input); + break; + } + case DbExpressionKind.GroupBy: + { + var exp = (DbGroupByExpression)expression; + var child = VisitInputWithBinding(exp.Input.Expression, exp.Input.VariableName); + + // I don't know why the input for GroupBy in EF have two names + RefToNode[exp.Input.GroupVariableName] = child; + + var input = child.Last.Exp; + var enterScope = false; + if (!IsCompatible(input, expression.ExpressionKind)) + input = new InputExpression(input, child.Last.AsName); + else enterScope = true; + + if (enterScope) EnterExpression(child); + + input.Projection = new CommaSeparatedExpression(); + + input.GroupBy = new GroupByExpression(); + var rowType = (RowType)((CollectionType)exp.ResultType.EdmType).TypeUsage.EdmType; + var columnIndex = 0; + foreach (var key in exp.Keys) + { + var keyColumnExpression = key.Accept(this); + var prop = rowType.Properties[columnIndex]; + input.Projection.Arguments.Add(new ColumnExpression(keyColumnExpression, prop.Name, prop.TypeUsage)); + // have no idea why EF is generating a group by with a constant expression, + // but postgresql doesn't need it. + if (!(key is DbConstantExpression)) + input.GroupBy.AppendGroupingKey(keyColumnExpression); + ++columnIndex; + } + foreach (var ag in exp.Aggregates) + { + var function = (DbFunctionAggregate)ag; + var functionExpression = VisitFunction(function); + var prop = rowType.Properties[columnIndex]; + input.Projection.Arguments.Add(new ColumnExpression(functionExpression, prop.Name, prop.TypeUsage)); + ++columnIndex; + } + + if (enterScope) LeaveExpression(child); + + n = new PendingProjectsNode(bindingName, input); + break; + } + case DbExpressionKind.CrossJoin: + case DbExpressionKind.FullOuterJoin: + case DbExpressionKind.InnerJoin: + case DbExpressionKind.LeftOuterJoin: + case DbExpressionKind.CrossApply: + case DbExpressionKind.OuterApply: + { + var input = new InputExpression(); + n = new PendingProjectsNode(bindingName, input); + + var from = VisitJoinChildren(expression, input, n); + + input.From = from; + + break; + } + case DbExpressionKind.Function: + { + var function = (DbFunctionExpression)expression; + var input = new InputExpression( + VisitFunction(function.Function, function.Arguments, function.ResultType), bindingName); + + n = new PendingProjectsNode(bindingName, input); + break; + } + default: + throw new NotImplementedException(); + } + + RefToNode[bindingName] = n; + return n; + } + + bool IsJoin(DbExpressionKind kind) + { + switch (kind) + { + case DbExpressionKind.CrossJoin: + case DbExpressionKind.FullOuterJoin: + case DbExpressionKind.InnerJoin: + case DbExpressionKind.LeftOuterJoin: + case DbExpressionKind.CrossApply: + case DbExpressionKind.OuterApply: + return true; + } + return false; + } + + JoinExpression VisitJoinChildren(DbExpression expression, InputExpression input, PendingProjectsNode n) + { + DbExpressionBinding left, right; + DbExpression condition = null; + if (expression.ExpressionKind == DbExpressionKind.CrossJoin) + { + left = ((DbCrossJoinExpression)expression).Inputs[0]; + right = ((DbCrossJoinExpression)expression).Inputs[1]; + if (((DbCrossJoinExpression)expression).Inputs.Count > 2) + { + // I have never seen more than 2 inputs in CrossJoin + throw new NotImplementedException(); + } + } + else if (expression.ExpressionKind == DbExpressionKind.CrossApply || expression.ExpressionKind == DbExpressionKind.OuterApply) + { + left = ((DbApplyExpression)expression).Input; + right = ((DbApplyExpression)expression).Apply; + } + else + { + left = ((DbJoinExpression)expression).Left; + right = ((DbJoinExpression)expression).Right; + condition = ((DbJoinExpression)expression).JoinCondition; + } + + return VisitJoinChildren(left.Expression, left.VariableName, right.Expression, right.VariableName, expression.ExpressionKind, condition, input, n); + } + + JoinExpression VisitJoinChildren(DbExpression left, string leftName, DbExpression right, string rightName, DbExpressionKind joinType, [CanBeNull] DbExpression condition, InputExpression input, PendingProjectsNode n) + { + var join = new JoinExpression { JoinType = joinType }; + + if (IsJoin(left.ExpressionKind)) + join.Left = VisitJoinChildren(left, input, n); + else + { + var l = VisitInputWithBinding(left, leftName); + l.JoinParent = n; + join.Left = new FromExpression(l.Last.Exp, l.Last.AsName); + } + + if (joinType == DbExpressionKind.OuterApply || joinType == DbExpressionKind.CrossApply) + { + EnterExpression(n); + var r = VisitInputWithBinding(right, rightName); + LeaveExpression(n); + r.JoinParent = n; + join.Right = new FromExpression(r.Last.Exp, r.Last.AsName) { ForceSubquery = true }; + } + else + { + if (IsJoin(right.ExpressionKind)) + join.Right = VisitJoinChildren(right, input, n); + else + { + var r = VisitInputWithBinding(right, rightName); + r.JoinParent = n; + join.Right = new FromExpression(r.Last.Exp, r.Last.AsName); + } + } + + if (condition != null) + { + EnterExpression(n); + join.Condition = condition.Accept(this); + LeaveExpression(n); + } + return join; + } + + public override VisitedExpression Visit([NotNull] DbVariableReferenceExpression expression) + { + //return new VariableReferenceExpression(expression.VariableName, _variableSubstitution); + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbUnionAllExpression expression) + { + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbTreatExpression expression) + { + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbSkipExpression expression) + { + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbSortExpression expression) + { + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbScanExpression expression) + { + MetadataProperty metadata; + string tableName; + var overrideTable = "http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Name"; + if (expression.Target.MetadataProperties.TryGetValue(overrideTable, false, out metadata) && metadata.Value != null) + tableName = metadata.Value.ToString(); + else if (expression.Target.MetadataProperties.TryGetValue("Table", false, out metadata) && metadata.Value != null) + tableName = metadata.Value.ToString(); + else + tableName = expression.Target.Name; + + if (expression.Target.MetadataProperties.Contains("DefiningQuery")) + { + var definingQuery = expression.Target.MetadataProperties.GetValue("DefiningQuery", false); + if (definingQuery.Value != null) + return new ScanExpression("(" + definingQuery.Value + ")", expression.Target); + } + + ScanExpression scan; + var overrideSchema = "http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Schema"; + if (expression.Target.MetadataProperties.TryGetValue(overrideSchema, false, out metadata) && metadata.Value != null) + { + var schema = metadata.Value.ToString(); + scan = string.IsNullOrEmpty(schema) + ? new ScanExpression(QuoteIdentifier(tableName), expression.Target) + : new ScanExpression(QuoteIdentifier(schema) + "." + QuoteIdentifier(tableName), expression.Target); + } + else if (expression.Target.MetadataProperties.TryGetValue("Schema", false, out metadata) && metadata.Value != null) + { + var schema = metadata.Value.ToString(); + scan = string.IsNullOrEmpty(schema) + ? new ScanExpression(QuoteIdentifier(tableName), expression.Target) + : new ScanExpression(QuoteIdentifier(schema) + "." + QuoteIdentifier(tableName), expression.Target); + } + else + { + var schema = expression.Target.EntityContainer.Name; + scan = string.IsNullOrEmpty(schema) + ? new ScanExpression(QuoteIdentifier(tableName), expression.Target) + : new ScanExpression(QuoteIdentifier(schema) + "." + QuoteIdentifier(tableName), expression.Target); + } + + return scan; + } + + public override VisitedExpression Visit([NotNull] DbRelationshipNavigationExpression expression) + { + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbRefExpression expression) + { + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbQuantifierExpression expression) + { + // TODO: EXISTS or NOT EXISTS depending on expression.ExpressionKind + // comes with it's built in test (subselect for EXISTS) + // This kind of expression is never even created in the EF6 code base + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbProjectExpression expression) + => VisitInputWithBinding(expression, NextAlias()).Last.Exp; + + // use parameter in sql + public override VisitedExpression Visit([NotNull] DbParameterReferenceExpression expression) + => new LiteralExpression("@" + expression.ParameterName); + + public override VisitedExpression Visit([NotNull] DbOrExpression expression) + => OperatorExpression.Build(Operator.Or, _useNewPrecedences, expression.Left.Accept(this), expression.Right.Accept(this)); + + public override VisitedExpression Visit([NotNull] DbOfTypeExpression expression) + { + throw new NotImplementedException(); + } + + // select does something different here. But insert, update, delete, and functions can just use + // a NULL literal. + public override VisitedExpression Visit([NotNull] DbNullExpression expression) + => new LiteralExpression("NULL"); + + // argument can be a "NOT EXISTS" or similar operator that can be negated. + // Convert the not if that's the case + public override VisitedExpression Visit([NotNull] DbNotExpression expression) + => OperatorExpression.Negate(expression.Argument.Accept(this), _useNewPrecedences); + + // Handled by VisitInputWithBinding + public override VisitedExpression Visit([NotNull] DbNewInstanceExpression expression) + { + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbLimitExpression expression) + { + // Normally handled by VisitInputWithBinding + + // Otherwise, it is (probably) a child of a DbElementExpression, + // in which case the child of this expression might be a DbProjectExpression, + // then the correct columns will be projected since Limit is compatible with the result of a DbProjectExpression, + // which will result in having a Projection on the node after visiting it. + var node = VisitInputWithBinding(expression, NextAlias()); + if (node.Last.Exp.Projection == null) + { + // This DbLimitExpression is (probably) a child of DbElementExpression + // and this expression's child is not a DbProjectExpression, but we should + // find a DbProjectExpression if we look deeper in the command tree. + // The child of this expression is (probably) a DbSortExpression or something else + // that will (probably) be an ancestor to a DbProjectExpression. + + // Since this is (probably) a child of DbElementExpression, we want the first column, + // so make sure it is propagated from the nearest explicit projection. + + var projection = node.Selects[0].Exp.Projection; + for (var i = 1; i < node.Selects.Count; i++) + { + var column = (ColumnExpression)projection.Arguments[0]; + node.Selects[i].Exp.ColumnsToProject[new StringPair(node.Selects[i - 1].AsName, column.Name)] = column.Name; + } + } + return node.Last.Exp; + } + + // LIKE keyword + public override VisitedExpression Visit([NotNull] DbLikeExpression expression) + => OperatorExpression.Build(Operator.Like, _useNewPrecedences, expression.Argument.Accept(this), expression.Pattern.Accept(this)); + + public override VisitedExpression Visit([NotNull] DbJoinExpression expression) + { + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbIsOfExpression expression) + { + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbIsNullExpression expression) + => OperatorExpression.Build(Operator.IsNull, _useNewPrecedences, expression.Argument.Accept(this)); + + // NOT EXISTS + public override VisitedExpression Visit([NotNull] DbIsEmptyExpression expression) + => OperatorExpression.Negate(new ExistsExpression(expression.Argument.Accept(this)), _useNewPrecedences); + + public override VisitedExpression Visit([NotNull] DbIntersectExpression expression) + { + // INTERSECT keyword + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + // Normally handled by VisitInputWithBinding + // Otherwise, it is (probably) a child of a DbElementExpression. + // Group by always projects the correct columns. + public override VisitedExpression Visit([NotNull] DbGroupByExpression expression) + => VisitInputWithBinding(expression, NextAlias()).Last.Exp; + + public override VisitedExpression Visit([NotNull] DbRefKeyExpression expression) + { + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbEntityRefExpression expression) + { + throw new NotImplementedException(); + } + + // a function call + // may be built in, canonical, or user defined + public override VisitedExpression Visit([NotNull] DbFunctionExpression expression) + => VisitFunction(expression.Function, expression.Arguments, expression.ResultType); + + public override VisitedExpression Visit([NotNull] DbFilterExpression expression) + { + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbExceptExpression expression) + { + // EXCEPT keyword + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbElementExpression expression) + { + // If child of DbNewInstanceExpression, this is handled in VisitInputWithBinding + + // a scalar expression (ie ExecuteScalar) + // so it will likely be translated into a select + //throw new NotImplementedException(); + var scalar = new LiteralExpression("("); + scalar.Append(expression.Argument.Accept(this)); + scalar.Append(")"); + return scalar; + } + + public override VisitedExpression Visit([NotNull] DbDistinctExpression expression) + { + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbDerefExpression expression) + { + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbCrossJoinExpression expression) + { + // join without ON + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbConstantExpression expression) + { + if (CreateParametersForConstants) + { + var parameter = new NpgsqlParameter + { + ParameterName = "p_" + ParameterCount++, + NpgsqlDbType = NpgsqlProviderManifest.GetNpgsqlDbType(((PrimitiveType)expression.ResultType.EdmType).PrimitiveTypeKind), + Value = expression.Value + }; + Command.Parameters.Add(parameter); + return new LiteralExpression("@" + parameter.ParameterName); + } + + return new ConstantExpression(expression.Value, expression.ResultType); + } + + public override VisitedExpression Visit([NotNull] DbComparisonExpression expression) + { + Operator comparisonOperator; + switch (expression.ExpressionKind) + { + case DbExpressionKind.Equals: comparisonOperator = Operator.Equals; break; + case DbExpressionKind.GreaterThan: comparisonOperator = Operator.GreaterThan; break; + case DbExpressionKind.GreaterThanOrEquals: comparisonOperator = Operator.GreaterThanOrEquals; break; + case DbExpressionKind.LessThan: comparisonOperator = Operator.LessThan; break; + case DbExpressionKind.LessThanOrEquals: comparisonOperator = Operator.LessThanOrEquals; break; + case DbExpressionKind.Like: comparisonOperator = Operator.Like; break; + case DbExpressionKind.NotEquals: comparisonOperator = Operator.NotEquals; break; + default: throw new NotSupportedException(); + } + return OperatorExpression.Build(comparisonOperator, _useNewPrecedences, expression.Left.Accept(this), expression.Right.Accept(this)); + } + + public override VisitedExpression Visit([NotNull] DbCastExpression expression) + => new CastExpression(expression.Argument.Accept(this), GetDbType(expression.ResultType.EdmType)); + + protected string GetDbType(EdmType edmType) + { + var primitiveType = edmType as PrimitiveType; + if (primitiveType == null) + throw new NotSupportedException(); + + switch (primitiveType.PrimitiveTypeKind) + { + case PrimitiveTypeKind.Boolean: + return "bool"; + case PrimitiveTypeKind.SByte: + case PrimitiveTypeKind.Byte: + case PrimitiveTypeKind.Int16: + return "int2"; + case PrimitiveTypeKind.Int32: + return "int4"; + case PrimitiveTypeKind.Int64: + return "int8"; + case PrimitiveTypeKind.String: + return "text"; + case PrimitiveTypeKind.Decimal: + return "numeric"; + case PrimitiveTypeKind.Single: + return "float4"; + case PrimitiveTypeKind.Double: + return "float8"; + case PrimitiveTypeKind.DateTime: + return "timestamp"; + case PrimitiveTypeKind.DateTimeOffset: + return "timestamptz"; + case PrimitiveTypeKind.Time: + return "interval"; + case PrimitiveTypeKind.Binary: + return "bytea"; + case PrimitiveTypeKind.Guid: + return "uuid"; + } + throw new NotSupportedException(); + } + + public override VisitedExpression Visit([NotNull] DbCaseExpression expression) + { + // Check for COALESCE like CASE + if (expression.When.Count == 1 && + expression.When[0].ExpressionKind == DbExpressionKind.IsNull) + { + var isNullExpression = (DbIsNullExpression)expression.When[0]; + if (isNullExpression.Argument.Equals(expression.Else)) + { + var coalesceExpression = new LiteralExpression("COALESCE("); + coalesceExpression.Append(expression.Else.Accept(this)); + coalesceExpression.Append(","); + coalesceExpression.Append(expression.Then[0].Accept(this)); + coalesceExpression.Append(")"); + return coalesceExpression; + } + } + + // General CASE + var caseExpression = new LiteralExpression(" CASE "); + for (var i = 0; i < expression.When.Count && i < expression.Then.Count; ++i) + { + caseExpression.Append(" WHEN ("); + caseExpression.Append(expression.When[i].Accept(this)); + caseExpression.Append(") THEN ("); + caseExpression.Append(expression.Then[i].Accept(this)); + caseExpression.Append(")"); + } + + if (expression.Else is DbNullExpression) + caseExpression.Append(" END "); + else + { + caseExpression.Append(" ELSE ("); + caseExpression.Append(expression.Else.Accept(this)); + caseExpression.Append(") END "); + } + + return caseExpression; + } + + public override VisitedExpression Visit([NotNull] DbArithmeticExpression expression) + { + Operator arithmeticOperator; + + switch (expression.ExpressionKind) + { + case DbExpressionKind.Divide: + arithmeticOperator = Operator.Div; + break; + case DbExpressionKind.Minus: + arithmeticOperator = Operator.Sub; + break; + case DbExpressionKind.Modulo: + arithmeticOperator = Operator.Mod; + break; + case DbExpressionKind.Multiply: + arithmeticOperator = Operator.Mul; + break; + case DbExpressionKind.Plus: + arithmeticOperator = Operator.Add; + break; + case DbExpressionKind.UnaryMinus: + arithmeticOperator = Operator.UnaryMinus; + break; + default: + throw new NotSupportedException(); + } + + if (expression.ExpressionKind == DbExpressionKind.UnaryMinus) + { + Debug.Assert(expression.Arguments.Count == 1); + return OperatorExpression.Build(arithmeticOperator, _useNewPrecedences, expression.Arguments[0].Accept(this)); + } + + Debug.Assert(expression.Arguments.Count == 2); + return OperatorExpression.Build(arithmeticOperator, _useNewPrecedences, expression.Arguments[0].Accept(this), expression.Arguments[1].Accept(this)); + } + + public override VisitedExpression Visit([NotNull] DbApplyExpression expression) + { + // like a join, but used when the right hand side (the Apply part) is a function. + // it lets you return the results of a function call given values from the + // left hand side (the Input part). + // sql standard is lateral join + + // Handled by VisitInputWithBinding + throw new NotImplementedException(); + } + + public override VisitedExpression Visit([NotNull] DbAndExpression expression) + => OperatorExpression.Build(Operator.And, _useNewPrecedences, expression.Left.Accept(this), expression.Right.Accept(this)); + + public override VisitedExpression Visit([NotNull] DbExpression expression) + { + // only concrete types visited + throw new NotSupportedException(); + } + + public abstract void BuildCommand(DbCommand command); + + internal static string QuoteIdentifier(string identifier) + => "\"" + identifier.Replace("\"", "\"\"") + "\""; + + VisitedExpression VisitFunction(DbFunctionAggregate functionAggregate) + { + if (functionAggregate.Function.NamespaceName == "Edm") + { + FunctionExpression aggregate; + try + { + aggregate = new FunctionExpression(AggregateFunctionNames[functionAggregate.Function.Name]); + } + 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); + return new CastExpression(aggregate, GetDbType(functionAggregate.ResultType.EdmType)); + } + throw new NotSupportedException(); + } + + VisitedExpression VisitFunction(EdmFunction function, IList args, TypeUsage resultType) + { + if (function.NamespaceName == "Edm") + { + VisitedExpression arg; + switch (function.Name) + { + // string functions + case "Concat": + Debug.Assert(args.Count == 2); + return OperatorExpression.Build(Operator.Concat, _useNewPrecedences, args[0].Accept(this), args[1].Accept(this)); + case "Contains": + Debug.Assert(args.Count == 2); + var contains = new FunctionExpression("position"); + arg = args[1].Accept(this); + arg.Append(" in "); + arg.Append(args[0].Accept(this)); + contains.AddArgument(arg); + // if position returns zero, then contains is false + return OperatorExpression.Build(Operator.GreaterThan, _useNewPrecedences, contains, new LiteralExpression("0")); + // case "EndsWith": - depends on a reverse function to be able to implement with parameterized queries + case "IndexOf": + Debug.Assert(args.Count == 2); + var indexOf = new FunctionExpression("position"); + arg = args[0].Accept(this); + arg.Append(" in "); + arg.Append(args[1].Accept(this)); + indexOf.AddArgument(arg); + return indexOf; + case "Left": + Debug.Assert(args.Count == 2); + return Substring(args[0].Accept(this), new LiteralExpression(" 1 "), args[1].Accept(this)); + case "Length": + var length = new FunctionExpression("char_length"); + Debug.Assert(args.Count == 1); + length.AddArgument(args[0].Accept(this)); + return new CastExpression(length, GetDbType(resultType.EdmType)); + case "LTrim": + return StringModifier("ltrim", args); + case "Replace": + var replace = new FunctionExpression("replace"); + Debug.Assert(args.Count == 3); + replace.AddArgument(args[0].Accept(this)); + replace.AddArgument(args[1].Accept(this)); + replace.AddArgument(args[2].Accept(this)); + return replace; + // case "Reverse": + case "Right": + Debug.Assert(args.Count == 2); + { + var arg0 = args[0].Accept(this); + var arg1 = args[1].Accept(this); + var start = new FunctionExpression("char_length"); + start.AddArgument(arg0); + // add one before subtracting count since strings are 1 based in postgresql + return Substring(arg0, OperatorExpression.Build(Operator.Sub, _useNewPrecedences, OperatorExpression.Build(Operator.Add, _useNewPrecedences, start, new LiteralExpression("1")), arg1)); + } + case "RTrim": + return StringModifier("rtrim", args); + case "Substring": + Debug.Assert(args.Count == 3); + return Substring(args[0].Accept(this), args[1].Accept(this), args[2].Accept(this)); + case "StartsWith": + Debug.Assert(args.Count == 2); + var startsWith = new FunctionExpression("position"); + arg = args[1].Accept(this); + arg.Append(" in "); + arg.Append(args[0].Accept(this)); + startsWith.AddArgument(arg); + return OperatorExpression.Build(Operator.Equals, _useNewPrecedences, startsWith, new LiteralExpression("1")); + case "ToLower": + return StringModifier("lower", args); + case "ToUpper": + return StringModifier("upper", args); + case "Trim": + return StringModifier("btrim", args); + + // date functions + // date functions + case "AddDays": + case "AddHours": + case "AddMicroseconds": + case "AddMilliseconds": + case "AddMinutes": + case "AddMonths": + case "AddNanoseconds": + case "AddSeconds": + case "AddYears": + return DateAdd(function.Name, args); + case "DiffDays": + case "DiffHours": + case "DiffMicroseconds": + case "DiffMilliseconds": + case "DiffMinutes": + case "DiffMonths": + case "DiffNanoseconds": + case "DiffSeconds": + case "DiffYears": + Debug.Assert(args.Count == 2); + return DateDiff(function.Name, args[0].Accept(this), args[1].Accept(this)); + case "Day": + case "Hour": + case "Minute": + case "Month": + case "Second": + case "Year": + return DatePart(function.Name, args); + case "Millisecond": + return DatePart("milliseconds", args); + case "GetTotalOffsetMinutes": + var timezone = DatePart("timezone", args); + return OperatorExpression.Build(Operator.Div, _useNewPrecedences, timezone, new LiteralExpression("60")); + case "CurrentDateTime": + return new LiteralExpression("LOCALTIMESTAMP"); + case "CurrentUtcDateTime": + var utcNow = new LiteralExpression("CURRENT_TIMESTAMP"); + utcNow.Append(" AT TIME ZONE 'UTC'"); + return utcNow; + case "CurrentDateTimeOffset": + // TODO: this doesn't work yet because the reader + // doesn't return DateTimeOffset. + return new LiteralExpression("CURRENT_TIMESTAMP"); + + // bitwise operators + case "BitwiseAnd": + return BitwiseOperator(args, Operator.BitwiseAnd); + case "BitwiseOr": + return BitwiseOperator(args, Operator.BitwiseOr); + case "BitwiseXor": + return BitwiseOperator(args, Operator.BitwiseXor); + case "BitwiseNot": + Debug.Assert(args.Count == 1); + return OperatorExpression.Build(Operator.BitwiseNot, _useNewPrecedences, args[0].Accept(this)); + + // math operators + case "Abs": + case "Ceiling": + case "Floor": + return UnaryMath(function.Name, args); + case "Round": + return args.Count == 1 ? UnaryMath(function.Name, args) : BinaryMath(function.Name, args); + case "Power": + return BinaryMath(function.Name, args); + case "Truncate": + return BinaryMath("trunc", args); + + case "NewGuid": + return new FunctionExpression("uuid_generate_v4"); + case "TruncateTime": + return new TruncateTimeExpression("day", args[0].Accept(this)); + + default: + throw new NotSupportedException("NotSupported " + function.Name); + } + } + + var functionName = function.StoreFunctionNameAttribute ?? function.Name; + if (function.NamespaceName == "Npgsql") + { + Operator binaryOperator; + if (BinaryOperatorFunctionNames.TryGetValue(functionName, out binaryOperator)) + { + if (args.Count != 2) + throw new ArgumentException($"Invalid number of {functionName} arguments. Expected 2.", nameof(args)); + + return OperatorExpression.Build( + binaryOperator, + _useNewPrecedences, + args[0].Accept(this), + args[1].Accept(this)); + } + + if (functionName == "operator_tsquery_negate") + { + if (args.Count != 1) + throw new ArgumentException("Invalid number of operator_tsquery_not arguments. Expected 1.", nameof(args)); + + return OperatorExpression.Build(Operator.QueryNegate, _useNewPrecedences, args[0].Accept(this)); + } + + if (functionName == "ts_rank" || functionName == "ts_rank_cd") + { + if (args.Count > 4) + { + var weightD = args[0] as DbConstantExpression; + var weightC = args[1] as DbConstantExpression; + var weightB = args[2] as DbConstantExpression; + var weightA = args[3] as DbConstantExpression; + + if (weightD == null || weightC == null || weightB == null || weightA == null) + throw new NotSupportedException("All weight values must be constant expressions."); + + var newValue = string.Format( + CultureInfo.InvariantCulture, + "{{ {0:r}, {1:r}, {2:r}, {3:r} }}", + weightD.Value, + weightC.Value, + weightB.Value, + weightA.Value); + + args = new[] { DbExpression.FromString(newValue) }.Concat(args.Skip(4)).ToList(); + } + } + else if (functionName == "setweight") + { + if (args.Count != 2) + throw new ArgumentException("Invalid number of setweight arguments. Expected 2.", nameof(args)); + + var weightLabelExpression = args[1] as DbConstantExpression; + if (weightLabelExpression == null) + throw new NotSupportedException("setweight label argument must be a constant expression."); + + var weightLabel = (NpgsqlWeightLabel)weightLabelExpression.Value; + if (!Enum.IsDefined(typeof(NpgsqlWeightLabel), weightLabelExpression.Value)) + throw new NotSupportedException("Unsupported weight label value: " + weightLabel); + + args = new[] { args[0], DbExpression.FromString(weightLabel.ToString()) }; + } + else if (functionName == "as_tsvector") + { + if (args.Count != 1) + throw new ArgumentException("Invalid number of arguments. Expected 1.", nameof(args)); + + return new CastExpression(args[0].Accept(this), "tsvector"); + } + else if (functionName == "as_tsquery") + { + if (args.Count != 1) + throw new ArgumentException("Invalid number of arguments. Expected 1.", nameof(args)); + + return new CastExpression(args[0].Accept(this), "tsquery"); + } + else if (functionName == "match_regex") + { + return VisitMatchRegex(function, args, resultType); + } + else if (functionName == "cast") + { + if (args.Count != 2) + throw new ArgumentException("Invalid number of arguments. Expected 2.", "args"); + + var typeNameExpression = args[1] as DbConstantExpression; + if (typeNameExpression == null) + throw new NotSupportedException("cast type name argument must be a constant expression."); + + return new CastExpression(args[0].Accept(this), typeNameExpression.Value.ToString()); + } + } + + var customFuncCall = new FunctionExpression( + string.IsNullOrEmpty(function.Schema) + ? QuoteIdentifier(functionName) + : QuoteIdentifier(function.Schema) + "." + QuoteIdentifier(functionName) + ); + + foreach (var a in args) + customFuncCall.AddArgument(a.Accept(this)); + return customFuncCall; + } + + VisitedExpression VisitMatchRegex(EdmFunction function, IList args, TypeUsage resultType) + { + if (args.Count != 2 && args.Count != 3) + throw new ArgumentException("Invalid number of arguments. Expected 2 or 3.", nameof(args)); + + var options = RegexOptions.None; + + if (args.Count == 3) + { + var optionsExpression = args[2] as DbConstantExpression; + if (optionsExpression == null) + throw new NotSupportedException("Options must be constant expression."); + + options = (RegexOptions)optionsExpression.Value; + } + + if (options.HasFlag(RegexOptions.RightToLeft) || options.HasFlag(RegexOptions.ECMAScript)) + { + throw new NotSupportedException("Options RightToLeft and ECMAScript are not supported."); + } + + if (options == RegexOptions.Singleline) + { + return OperatorExpression.Build( + Operator.RegexMatch, + _useNewPrecedences, + args[0].Accept(this), + args[1].Accept(this)); + } + + var flags = new StringBuilder("(?"); + + if (options.HasFlag(RegexOptions.IgnoreCase)) + { + flags.Append('i'); + } + + if (options.HasFlag(RegexOptions.Multiline)) + { + flags.Append('n'); + } + else if (!options.HasFlag(RegexOptions.Singleline)) + { + // In .NET's default mode, . doesn't match newlines but PostgreSQL it does. + flags.Append('p'); + } + + if (options.HasFlag(RegexOptions.IgnorePatternWhitespace)) + { + flags.Append('x'); + } + + flags.Append(')'); + + var primitiveType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String); + var newRegexExpression = OperatorExpression.Build( + Operator.Concat, + _useNewPrecedences, + new ConstantExpression(flags.ToString(), TypeUsage.CreateStringTypeUsage(primitiveType, true, false)), + args[1].Accept(this)); + + return OperatorExpression.Build( + Operator.RegexMatch, + _useNewPrecedences, + args[0].Accept(this), + newRegexExpression); + } + + VisitedExpression Substring(VisitedExpression source, VisitedExpression start, VisitedExpression count) + { + var substring = new FunctionExpression("substr"); + substring.AddArgument(source); + substring.AddArgument(start); + substring.AddArgument(count); + return substring; + } + + VisitedExpression Substring(VisitedExpression source, VisitedExpression start) + { + var substring = new FunctionExpression("substr"); + substring.AddArgument(source); + substring.AddArgument(start); + return substring; + } + + VisitedExpression UnaryMath(string funcName, IList args) + { + var mathFunction = new FunctionExpression(funcName); + Debug.Assert(args.Count == 1); + mathFunction.AddArgument(args[0].Accept(this)); + return mathFunction; + } + + VisitedExpression BinaryMath(string funcName, IList args) + { + var mathFunction = new FunctionExpression(funcName); + Debug.Assert(args.Count == 2); + mathFunction.AddArgument(args[0].Accept(this)); + mathFunction.AddArgument(args[1].Accept(this)); + return mathFunction; + } + + VisitedExpression StringModifier(string modifier, IList args) + { + var modifierFunction = new FunctionExpression(modifier); + Debug.Assert(args.Count == 1); + modifierFunction.AddArgument(args[0].Accept(this)); + return modifierFunction; + } + + VisitedExpression DatePart(string partName, IList args) + { + var extractDate = new FunctionExpression("cast(extract"); + Debug.Assert(args.Count == 1); + VisitedExpression arg = new LiteralExpression(partName + " FROM "); + arg.Append(args[0].Accept(this)); + extractDate.AddArgument(arg); + // need to convert to Int32 to match cononical function + extractDate.Append(" as int4)"); + return extractDate; + } + + /// + /// PostgreSQL has no direct functions to implements DateTime canonical functions + /// http://msdn.microsoft.com/en-us/library/bb738626.aspx + /// http://msdn.microsoft.com/en-us/library/bb738626.aspx + /// but we can use workaround: + /// expression + number * INTERVAL '1 number_type' + /// where number_type is the number type (days, years and etc) + /// + /// + /// + /// + VisitedExpression DateAdd(string functionName, IList args) + { + var nano = false; + var part = functionName.Substring(3); + + if (part == "Nanoseconds") + { + nano = true; + part = "Microseconds"; + } + + Debug.Assert(args.Count == 2); + var time = args[0].Accept(this); + var mulLeft = args[1].Accept(this); + if (nano) + mulLeft = OperatorExpression.Build(Operator.Div, _useNewPrecedences, mulLeft, new LiteralExpression("1000")); + var mulRight = new LiteralExpression($"INTERVAL '1 {part}'"); + return OperatorExpression.Build(Operator.Add, _useNewPrecedences, time, OperatorExpression.Build(Operator.Mul, _useNewPrecedences, mulLeft, mulRight)); + } + + VisitedExpression DateDiff(string functionName, VisitedExpression start, VisitedExpression end) + { + switch (functionName) + { + case "DiffDays": + start = new FunctionExpression("date_trunc").AddArgument("'day'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'day'").AddArgument(end); + return new FunctionExpression("date_part").AddArgument("'day'").AddArgument( + OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start) + ).Append("::int4"); + case "DiffHours": + { + start = new FunctionExpression("date_trunc").AddArgument("'hour'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'hour'").AddArgument(end); + var epoch = new LiteralExpression("epoch from "); + var diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); + epoch.Append(diff); + return OperatorExpression.Build(Operator.Div, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch).Append("::int4"), new LiteralExpression("3600")); + } + case "DiffMicroseconds": + { + start = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(end); + var epoch = new LiteralExpression("epoch from "); + var diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); + epoch.Append(diff); + return new CastExpression(OperatorExpression.Build(Operator.Mul, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch), new LiteralExpression("1000000")), "int4"); + } + case "DiffMilliseconds": + { + start = new FunctionExpression("date_trunc").AddArgument("'milliseconds'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'milliseconds'").AddArgument(end); + var epoch = new LiteralExpression("epoch from "); + var diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); + epoch.Append(diff); + return new CastExpression(OperatorExpression.Build(Operator.Mul, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch), new LiteralExpression("1000")), "int4"); + } + case "DiffMinutes": + { + start = new FunctionExpression("date_trunc").AddArgument("'minute'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'minute'").AddArgument(end); + var epoch = new LiteralExpression("epoch from "); + var diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); + epoch.Append(diff); + return OperatorExpression.Build(Operator.Div, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch).Append("::int4"), new LiteralExpression("60")); + } + case "DiffMonths": + { + start = new FunctionExpression("date_trunc").AddArgument("'month'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'month'").AddArgument(end); + var age = new FunctionExpression("age").AddArgument(end).AddArgument(start); + + // A month is 30 days and a year is 365.25 days after conversion from interval to seconds. + // After rounding and casting, the result will contain the correct number of months as an int4. + var seconds = new FunctionExpression("extract").AddArgument(new LiteralExpression("epoch from ").Append(age)); + var months = OperatorExpression.Build(Operator.Div, _useNewPrecedences, seconds, new LiteralExpression("2629800.0")); + return new FunctionExpression("round").AddArgument(months).Append("::int4"); + } + case "DiffNanoseconds": + { + // PostgreSQL only supports microseconds precision, so the value will be a multiple of 1000 + // This date_trunc will make sure start and end are of type timestamp, e.g. if the arguments is of type date + start = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(end); + var epoch = new LiteralExpression("epoch from "); + var diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); + epoch.Append(diff); + return new CastExpression(OperatorExpression.Build(Operator.Mul, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch), new LiteralExpression("1000000000")), "int4"); + } + case "DiffSeconds": + { + start = new FunctionExpression("date_trunc").AddArgument("'second'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'second'").AddArgument(end); + var epoch = new LiteralExpression("epoch from "); + var diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); + epoch.Append(diff); + return new FunctionExpression("extract").AddArgument(epoch).Append("::int4"); + } + case "DiffYears": + { + start = new FunctionExpression("date_trunc").AddArgument("'year'").AddArgument(start); + end = new FunctionExpression("date_trunc").AddArgument("'year'").AddArgument(end); + var age = new FunctionExpression("age").AddArgument(end).AddArgument(start); + return new FunctionExpression("date_part").AddArgument("'year'").AddArgument(age).Append("::int4"); + } + default: + throw new NotSupportedException("Internal error: unknown function name " + functionName); + } + } + + VisitedExpression BitwiseOperator(IList args, Operator oper) + { + Debug.Assert(args.Count == 2); + return OperatorExpression.Build(oper, _useNewPrecedences, args[0].Accept(this), args[1].Accept(this)); + } + + public override VisitedExpression Visit([NotNull] DbInExpression expression) + { + var item = expression.Item.Accept(this); + + var elements = new ConstantExpression[expression.List.Count]; + for (var i = 0; i < expression.List.Count; i++) + elements[i] = (ConstantExpression)expression.List[i].Accept(this); + + return OperatorExpression.Build(Operator.In, _useNewPrecedences, item, new ConstantListExpression(elements)); + } + + public override VisitedExpression Visit([NotNull] DbPropertyExpression expression) + { + // This is overridden in the other visitors + throw new NotImplementedException("New in Entity Framework 6"); + } + } +} diff --git a/EF6.PG/SqlGenerators/SqlDeleteGenerator.cs b/EF6.PG/SqlGenerators/SqlDeleteGenerator.cs new file mode 100644 index 0000000..a24eb92 --- /dev/null +++ b/EF6.PG/SqlGenerators/SqlDeleteGenerator.cs @@ -0,0 +1,37 @@ +using System; +using System.Data.Common; +using System.Data.Entity.Core.Common.CommandTrees; + +namespace Npgsql.SqlGenerators +{ + internal class SqlDeleteGenerator : SqlBaseGenerator + { + readonly DbDeleteCommandTree _commandTree; + string _tableName; + + public SqlDeleteGenerator(DbDeleteCommandTree commandTree) + { + _commandTree = commandTree; + } + + public override VisitedExpression Visit(DbPropertyExpression expression) + { + var variable = expression.Instance as DbVariableReferenceExpression; + if (variable == null || variable.VariableName != _tableName) + throw new NotSupportedException(); + return new PropertyExpression(expression.Property); + } + + public override void BuildCommand(DbCommand command) + { + // TODO: handle _commandTree.Returning and _commandTree.Parameters + var delete = new DeleteExpression(); + _tableName = _commandTree.Target.VariableName; + delete.AppendFrom(_commandTree.Target.Expression.Accept(this)); + if (_commandTree.Predicate != null) + delete.AppendWhere(_commandTree.Predicate.Accept(this)); + _tableName = null; + command.CommandText = delete.ToString(); + } + } +} diff --git a/EF6.PG/SqlGenerators/SqlFunctionGenerator.cs b/EF6.PG/SqlGenerators/SqlFunctionGenerator.cs new file mode 100644 index 0000000..25ed47c --- /dev/null +++ b/EF6.PG/SqlGenerators/SqlFunctionGenerator.cs @@ -0,0 +1,22 @@ +using System.Linq; +using System.Data.Common; +using System.Data.Entity.Core.Common.CommandTrees; + +namespace Npgsql.SqlGenerators +{ + class SqlFunctionGenerator : SqlBaseGenerator + { + readonly DbFunctionCommandTree _commandTree; + + public SqlFunctionGenerator(DbFunctionCommandTree commandTree) + { + _commandTree = commandTree; + } + + public override void BuildCommand(DbCommand command) + { + var paramStr = string.Join(",", command.Parameters.OfType().Select(x => "@" + x.ParameterName).ToArray()); + command.CommandText = $"SELECT * FROM { QuoteIdentifier(_commandTree.EdmFunction.Schema) }.{ QuoteIdentifier(_commandTree.EdmFunction.Name) } ({paramStr})"; + } + } +} diff --git a/EF6.PG/SqlGenerators/SqlInsertGenerator.cs b/EF6.PG/SqlGenerators/SqlInsertGenerator.cs new file mode 100644 index 0000000..856a31a --- /dev/null +++ b/EF6.PG/SqlGenerators/SqlInsertGenerator.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Data.Entity.Core.Common.CommandTrees; + +namespace Npgsql.SqlGenerators +{ + internal class SqlInsertGenerator : SqlBaseGenerator + { + readonly DbInsertCommandTree _commandTree; + string _tableName; + + public SqlInsertGenerator(DbInsertCommandTree commandTree) + { + _commandTree = commandTree; + } + + public override VisitedExpression Visit(DbPropertyExpression expression) + { + var variable = expression.Instance as DbVariableReferenceExpression; + if (variable == null || variable.VariableName != _tableName) + throw new NotSupportedException(); + return new PropertyExpression(expression.Property); + } + + public override void BuildCommand(DbCommand command) + { + // TODO: handle_commandTree.Parameters + var insert = new InsertExpression(); + _tableName = _commandTree.Target.VariableName; + insert.AppendTarget(_commandTree.Target.Expression.Accept(this)); + var columns = new List(); + var values = new List(); + foreach (DbSetClause clause in _commandTree.SetClauses) + { + columns.Add(clause.Property.Accept(this)); + values.Add(clause.Value.Accept(this)); + } + insert.AppendColumns(columns); + insert.AppendValues(values); + if (_commandTree.Returning != null) + insert.AppendReturning((DbNewInstanceExpression)_commandTree.Returning); + _tableName = null; + command.CommandText = insert.ToString(); + } + } +} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/SqlSelectGenerator.cs b/EF6.PG/SqlGenerators/SqlSelectGenerator.cs similarity index 64% rename from src/EntityFramework6.Npgsql/SqlGenerators/SqlSelectGenerator.cs rename to EF6.PG/SqlGenerators/SqlSelectGenerator.cs index ef4e8ba..ed2616a 100644 --- a/src/EntityFramework6.Npgsql/SqlGenerators/SqlSelectGenerator.cs +++ b/EF6.PG/SqlGenerators/SqlSelectGenerator.cs @@ -1,43 +1,15 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; +using System; using System.Linq; -using System.Collections.Generic; using System.Data.Common; -#if ENTITIES6 +using System.Diagnostics; using System.Data.Entity.Core.Common.CommandTrees; using System.Data.Entity.Core.Metadata.Edm; -#else -using System.Data.Common.CommandTrees; -using System.Data.Metadata.Edm; -#endif namespace Npgsql.SqlGenerators { internal class SqlSelectGenerator : SqlBaseGenerator { - private DbQueryCommandTree _commandTree; + readonly DbQueryCommandTree _commandTree; public SqlSelectGenerator(DbQueryCommandTree commandTree) { @@ -74,18 +46,18 @@ public override VisitedExpression Visit(DbPropertyExpression expression) * The new name is then propagated down to the root. */ - string name = expression.Property.Name; - string from = (expression.Instance.ExpressionKind == DbExpressionKind.Property) + var name = expression.Property.Name; + var from = expression.Instance.ExpressionKind == DbExpressionKind.Property ? ((DbPropertyExpression)expression.Instance).Property.Name : ((DbVariableReferenceExpression)expression.Instance).VariableName; - PendingProjectsNode node = _refToNode[from]; + var node = RefToNode[from]; from = node.TopName; while (node != null) { foreach (var item in node.Selects) { - if (_currentExpressions.Contains(item.Exp)) + if (CurrentExpressions.Contains(item.Exp)) continue; var use = new StringPair(from, name); @@ -99,9 +71,7 @@ public override VisitedExpression Visit(DbPropertyExpression expression) item.Exp.ProjectNewNames.Add(name); } else - { name = item.Exp.ColumnsToProject[use]; - } from = item.AsName; } node = node.JoinParent; @@ -109,20 +79,18 @@ public override VisitedExpression Visit(DbPropertyExpression expression) return new ColumnReferenceExpression { Variable = from, Name = name }; } + // must provide a NULL of the correct type + // this is necessary for certain types of union queries. public override VisitedExpression Visit(DbNullExpression expression) - { - // must provide a NULL of the correct type - // this is necessary for certain types of union queries. - return new CastExpression(new LiteralExpression("NULL"), GetDbType(expression.ResultType.EdmType)); - } + => new CastExpression(new LiteralExpression("NULL"), GetDbType(expression.ResultType.EdmType)); public override void BuildCommand(DbCommand command) { - System.Diagnostics.Debug.Assert(command is NpgsqlCommand); - System.Diagnostics.Debug.Assert(_commandTree.Query is DbProjectExpression); - VisitedExpression ve = _commandTree.Query.Accept(this); - System.Diagnostics.Debug.Assert(ve is InputExpression); - InputExpression pe = (InputExpression)ve; + Debug.Assert(command is NpgsqlCommand); + Debug.Assert(_commandTree.Query is DbProjectExpression); + var ve = _commandTree.Query.Accept(this); + Debug.Assert(ve is InputExpression); + var pe = (InputExpression)ve; command.CommandText = pe.ToString(); // We retrieve all strings as unknowns in text format in the case the data types aren't really texts @@ -134,17 +102,18 @@ public override void BuildCommand(DbCommand command) return kind == PrimitiveTypeKind.SByte || kind == PrimitiveTypeKind.DateTimeOffset; })) { - ((NpgsqlCommand)command).ObjectResultTypes = pe.Projection.Arguments.Select(a => { + ((NpgsqlCommand)command).ObjectResultTypes = pe.Projection.Arguments.Select(a => + { var kind = ((PrimitiveType)((ColumnExpression)a).ColumnType.EdmType).PrimitiveTypeKind; - if (kind == PrimitiveTypeKind.SByte) + switch (kind) { + case PrimitiveTypeKind.SByte: return typeof(sbyte); - } - else if (kind == PrimitiveTypeKind.DateTimeOffset) - { + case PrimitiveTypeKind.DateTimeOffset: return typeof(DateTimeOffset); + default: + return null; } - return null; }).ToArray(); } } diff --git a/EF6.PG/SqlGenerators/SqlUpdateGenerator.cs b/EF6.PG/SqlGenerators/SqlUpdateGenerator.cs new file mode 100644 index 0000000..acf3fb3 --- /dev/null +++ b/EF6.PG/SqlGenerators/SqlUpdateGenerator.cs @@ -0,0 +1,41 @@ +using System; +using System.Data.Common; +using System.Data.Entity.Core.Common.CommandTrees; + +namespace Npgsql.SqlGenerators +{ + class SqlUpdateGenerator : SqlBaseGenerator + { + readonly DbUpdateCommandTree _commandTree; + string _tableName; + + public SqlUpdateGenerator(DbUpdateCommandTree commandTree) + { + _commandTree = commandTree; + } + + public override VisitedExpression Visit(DbPropertyExpression expression) + { + var variable = expression.Instance as DbVariableReferenceExpression; + if (variable == null || variable.VariableName != _tableName) + throw new NotSupportedException(); + return new PropertyExpression(expression.Property); + } + + public override void BuildCommand(DbCommand command) + { + // TODO: handle _commandTree.Parameters + var update = new UpdateExpression(); + _tableName = _commandTree.Target.VariableName; + update.AppendTarget(_commandTree.Target.Expression.Accept(this)); + foreach (DbSetClause clause in _commandTree.SetClauses) + update.AppendSet(clause.Property.Accept(this), clause.Value.Accept(this)); + if (_commandTree.Predicate != null) + update.AppendWhere(_commandTree.Predicate.Accept(this)); + if (_commandTree.Returning != null) + update.AppendReturning((DbNewInstanceExpression)_commandTree.Returning); + _tableName = null; + command.CommandText = update.ToString(); + } + } +} diff --git a/EF6.PG/SqlGenerators/StringPair.cs b/EF6.PG/SqlGenerators/StringPair.cs new file mode 100644 index 0000000..72d2512 --- /dev/null +++ b/EF6.PG/SqlGenerators/StringPair.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using JetBrains.Annotations; + +namespace Npgsql.SqlGenerators +{ + /// + /// Used for lookup in a Dictionary, since Tuple is not available in .NET 3.5 + /// + internal class StringPair + { + public string Item1 { get; } + public string Item2 { get; } + + public StringPair(string s1, string s2) + { + Item1 = s1; + Item2 = s2; + } + + public override bool Equals([CanBeNull] object obj) + { + var o = obj as StringPair; + if (o == null) + return false; + + return Item1 == o.Item1 && Item2 == o.Item2; + } + + public override int GetHashCode() + => (Item1 + "." + Item2).GetHashCode(); + } +} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/VisitedExpression.cs b/EF6.PG/SqlGenerators/VisitedExpression.cs similarity index 57% rename from src/EntityFramework6.Npgsql/SqlGenerators/VisitedExpression.cs rename to EF6.PG/SqlGenerators/VisitedExpression.cs index 0f468d0..b952deb 100644 --- a/src/EntityFramework6.Npgsql/SqlGenerators/VisitedExpression.cs +++ b/EF6.PG/SqlGenerators/VisitedExpression.cs @@ -1,40 +1,12 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; -#if ENTITIES6 using System.Data.Entity.Core.Common.CommandTrees; using System.Data.Entity.Core.Metadata.Edm; -#else -using System.Data.Metadata.Edm; -using System.Data.Common.CommandTrees; -#endif using NpgsqlTypes; -using System.Data; using System.Globalization; +using JetBrains.Annotations; namespace Npgsql.SqlGenerators { @@ -59,25 +31,23 @@ public VisitedExpression Append(string literal) public override string ToString() { - StringBuilder sqlText = new StringBuilder(); + var sqlText = new StringBuilder(); WriteSql(sqlText); return sqlText.ToString(); } - protected List ExpressionList { get; private set; } + protected List ExpressionList { get; } internal virtual void WriteSql(StringBuilder sqlText) { - foreach (VisitedExpression expression in ExpressionList) - { + foreach (var expression in ExpressionList) expression.WriteSql(sqlText); - } } } internal class LiteralExpression : VisitedExpression { - private string _literal; + readonly string _literal; public LiteralExpression(string literal) { @@ -108,7 +78,7 @@ internal class CommaSeparatedExpression : VisitedExpression internal override void WriteSql(StringBuilder sqlText) { - for (int i = 0; i < Arguments.Count; ++i) + for (var i = 0; i < Arguments.Count; ++i) { if (i != 0) sqlText.Append(", "); @@ -120,15 +90,15 @@ internal override void WriteSql(StringBuilder sqlText) internal class ConstantExpression : VisitedExpression { - private PrimitiveTypeKind _primitiveType; - private object _value; + readonly PrimitiveTypeKind _primitiveType; + readonly object _value; public ConstantExpression(object value, TypeUsage edmType) { if (edmType == null) - throw new ArgumentNullException("edmType"); + throw new ArgumentNullException(nameof(edmType)); if (edmType.EdmType == null || edmType.EdmType.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType) - throw new ArgumentException("Require primitive EdmType", "edmType"); + throw new ArgumentException("Require primitive EdmType", nameof(edmType)); _primitiveType = ((PrimitiveType)edmType.EdmType).PrimitiveTypeKind; _value = value; } @@ -136,120 +106,88 @@ public ConstantExpression(object value, TypeUsage edmType) internal override void WriteSql(StringBuilder sqlText) { var ni = CultureInfo.InvariantCulture.NumberFormat; - object value = _value; + var value = _value; switch (_primitiveType) { - case PrimitiveTypeKind.Binary: - { - sqlText.AppendFormat("decode('{0}', 'base64')", Convert.ToBase64String((byte[])_value)); - } - break; - case PrimitiveTypeKind.DateTime: - sqlText.AppendFormat(ni, "TIMESTAMP '{0:o}'", _value); - break; - case PrimitiveTypeKind.DateTimeOffset: - sqlText.AppendFormat(ni, "TIMESTAMP WITH TIME ZONE '{0:o}'", _value); - break; - case PrimitiveTypeKind.Decimal: - if ((decimal)_value < 0) - { - sqlText.AppendFormat(ni, "({0})::numeric", _value); - } - else - { - sqlText.AppendFormat(ni, "{0}::numeric", _value); - } - break; - case PrimitiveTypeKind.Double: - if (double.IsNaN((double)_value)) - { - sqlText.AppendFormat("'NaN'::float8"); - } - else if (double.IsPositiveInfinity((double)_value)) - { - sqlText.AppendFormat("'Infinity'::float8"); - } - else if (double.IsNegativeInfinity((double)_value)) - { - sqlText.AppendFormat("'-Infinity'::float8"); - } - else if ((double)_value < 0) - { - sqlText.AppendFormat(ni, "({0:r})::float8", _value); - } - else - { - sqlText.AppendFormat(ni, "{0:r}::float8", _value); - } - break; - // PostgreSQL has no support for bytes. int2 is used instead in Npgsql. - case PrimitiveTypeKind.Byte: - value = (short)(byte)_value; - goto case PrimitiveTypeKind.Int16; - case PrimitiveTypeKind.SByte: - value = (short)(sbyte)_value; - goto case PrimitiveTypeKind.Int16; - case PrimitiveTypeKind.Int16: - if ((short)value < 0) - { - sqlText.AppendFormat(ni, "({0})::int2", _value); - } - else - { - sqlText.AppendFormat(ni, "{0}::int2", _value); - } - break; - case PrimitiveTypeKind.Int32: - sqlText.AppendFormat(ni, "{0}", _value); - break; - case PrimitiveTypeKind.Int64: - if ((long)_value < 0) - { - sqlText.AppendFormat(ni, "({0})::int8", _value); - } - else - { - sqlText.AppendFormat(ni, "{0}::int8", _value); - } - break; - case PrimitiveTypeKind.Single: - if (float.IsNaN((float)_value)) - { - sqlText.AppendFormat("'NaN'::float4"); - } - else if (float.IsPositiveInfinity((float)_value)) - { - sqlText.AppendFormat("'Infinity'::float4"); - } - else if (float.IsNegativeInfinity((float)_value)) - { - sqlText.AppendFormat("'-Infinity'::float4"); - } - else if ((float)_value < 0) - { - sqlText.AppendFormat(ni, "({0:r})::float4", _value); - } - else - { - sqlText.AppendFormat(ni, "{0:r}::float4", _value); - } - break; - case PrimitiveTypeKind.Boolean: - sqlText.Append(((bool)_value) ? "TRUE" : "FALSE"); - break; - case PrimitiveTypeKind.Guid: - sqlText.Append('\'').Append((Guid)_value).Append('\''); - sqlText.Append("::uuid"); - break; - case PrimitiveTypeKind.String: - sqlText.Append("E'").Append(((string)_value).Replace(@"\", @"\\").Replace("'", @"\'")).Append("'"); - break; - case PrimitiveTypeKind.Time: - sqlText.AppendFormat(ni, "INTERVAL '{0}'", (NpgsqlTimeSpan)(TimeSpan)_value); - break; - default: - // TODO: must support more constant value types. - throw new NotSupportedException(string.Format("NotSupported: {0} {1}", _primitiveType, _value)); + case PrimitiveTypeKind.Binary: + { + sqlText.Append($"decode('{Convert.ToBase64String((byte[])_value)}', 'base64')"); + } + break; + case PrimitiveTypeKind.DateTime: + sqlText.AppendFormat(ni, "TIMESTAMP '{0:o}'", _value); + break; + case PrimitiveTypeKind.DateTimeOffset: + sqlText.AppendFormat(ni, "TIMESTAMP WITH TIME ZONE '{0:o}'", _value); + break; + case PrimitiveTypeKind.Decimal: + sqlText.AppendFormat(ni, (decimal)_value < 0 + ? "({0})::numeric" + : "{0}::numeric", _value + ); + break; + case PrimitiveTypeKind.Double: + if (double.IsNaN((double)_value)) + sqlText.Append("'NaN'::float8"); + else if (double.IsPositiveInfinity((double)_value)) + sqlText.Append("'Infinity'::float8"); + else if (double.IsNegativeInfinity((double)_value)) + sqlText.Append("'-Infinity'::float8"); + else if ((double)_value < 0) + sqlText.AppendFormat(ni, "({0:r})::float8", _value); + else + sqlText.AppendFormat(ni, "{0:r}::float8", _value); + break; + // PostgreSQL has no support for bytes. int2 is used instead in Npgsql. + case PrimitiveTypeKind.Byte: + value = (short)(byte)_value; + goto case PrimitiveTypeKind.Int16; + case PrimitiveTypeKind.SByte: + value = (short)(sbyte)_value; + goto case PrimitiveTypeKind.Int16; + case PrimitiveTypeKind.Int16: + sqlText.AppendFormat(ni, (short)value < 0 + ? "({0})::int2" + : "{0}::int2", _value + ); + break; + case PrimitiveTypeKind.Int32: + sqlText.AppendFormat(ni, "{0}", _value); + break; + case PrimitiveTypeKind.Int64: + sqlText.AppendFormat(ni, (long)_value < 0 + ? "({0})::int8" + : "{0}::int8", _value + ); + break; + case PrimitiveTypeKind.Single: + if (float.IsNaN((float)_value)) + sqlText.Append("'NaN'::float4"); + else if (float.IsPositiveInfinity((float)_value)) + sqlText.Append("'Infinity'::float4"); + else if (float.IsNegativeInfinity((float)_value)) + sqlText.Append("'-Infinity'::float4"); + else if ((float)_value < 0) + sqlText.AppendFormat(ni, "({0:r})::float4", _value); + else + sqlText.AppendFormat(ni, "{0:r}::float4", _value); + break; + case PrimitiveTypeKind.Boolean: + sqlText.Append((bool)_value ? "TRUE" : "FALSE"); + break; + case PrimitiveTypeKind.Guid: + sqlText.Append('\'').Append((Guid)_value).Append('\''); + sqlText.Append("::uuid"); + break; + case PrimitiveTypeKind.String: + sqlText.Append("E'").Append(((string)_value).Replace(@"\", @"\\").Replace("'", @"\'")).Append("'"); + break; + case PrimitiveTypeKind.Time: + sqlText.AppendFormat(ni, "INTERVAL '{0}'", (NpgsqlTimeSpan)(TimeSpan)_value); + break; + default: + // TODO: must support more constant value types. + throw new NotSupportedException($"NotSupported: {_primitiveType} {_value}"); } base.WriteSql(sqlText); } @@ -268,8 +206,8 @@ public void AppendColumns(IEnumerable columns) return; Append("("); - bool first = true; - foreach (VisitedExpression expression in columns) + var first = true; + foreach (var expression in columns) { if (!first) Append(","); @@ -285,7 +223,7 @@ public void AppendValues(IEnumerable columns) { Append(" VALUES ("); bool first = true; - foreach (VisitedExpression expression in columns) + foreach (var expression in columns) { if (!first) Append(","); @@ -295,20 +233,18 @@ public void AppendValues(IEnumerable columns) Append(")"); } else - { Append(" DEFAULT VALUES"); - } } internal void AppendReturning(DbNewInstanceExpression expression) { Append(" RETURNING ");//Don't put () around columns it will probably have unwanted effect - bool first = true; + var first = true; foreach (var returingProperty in expression.Arguments) { if (!first) Append(","); - Append(SqlBaseGenerator.QuoteIdentifier((returingProperty as DbPropertyExpression).Property.Name)); + Append(SqlBaseGenerator.QuoteIdentifier(((DbPropertyExpression)returingProperty).Property.Name)); first = false; } } @@ -322,7 +258,7 @@ internal override void WriteSql(StringBuilder sqlText) internal class UpdateExpression : VisitedExpression { - private bool _setSeperatorRequired; + bool _setSeperatorRequired; public void AppendTarget(VisitedExpression target) { @@ -331,10 +267,7 @@ public void AppendTarget(VisitedExpression target) public void AppendSet(VisitedExpression property, VisitedExpression value) { - if (_setSeperatorRequired) - Append(","); - else - Append(" SET "); + Append(_setSeperatorRequired ? "," : " SET "); Append(property); Append("="); Append(value); @@ -356,12 +289,12 @@ internal override void WriteSql(StringBuilder sqlText) internal void AppendReturning(DbNewInstanceExpression expression) { Append(" RETURNING ");//Don't put () around columns it will probably have unwanted effect - bool first = true; + var first = true; foreach (var returingProperty in expression.Arguments) { if (!first) Append(","); - Append(SqlBaseGenerator.QuoteIdentifier((returingProperty as DbPropertyExpression).Property.Name)); + Append(SqlBaseGenerator.QuoteIdentifier(((DbPropertyExpression)returingProperty).Property.Name)); first = false; } } @@ -389,31 +322,23 @@ internal override void WriteSql(StringBuilder sqlText) internal class ColumnExpression : VisitedExpression { - private VisitedExpression _column; - private string _columnName; - private TypeUsage _columnType; + internal string Name { get; } + internal TypeUsage ColumnType { get; } + readonly VisitedExpression _column; public ColumnExpression(VisitedExpression column, string columnName, TypeUsage columnType) { _column = column; - _columnName = columnName; - _columnType = columnType; + Name = columnName; + ColumnType = columnType; } - public string Name { get { return _columnName; } } - internal TypeUsage ColumnType { get { return _columnType; ;} } - - public Type CLRType + public Type ClrType { get { - if (_columnType == null) - return null; - PrimitiveType pt = _columnType.EdmType as PrimitiveType; - if (pt != null) - return pt.ClrEquivalentType; - else - return null; + var pt = ColumnType?.EdmType as PrimitiveType; + return pt?.ClrEquivalentType; } } @@ -421,11 +346,11 @@ internal override void WriteSql(StringBuilder sqlText) { _column.WriteSql(sqlText); - ColumnReferenceExpression column = _column as ColumnReferenceExpression; - if (column == null || column.Name != _columnName) + var column = _column as ColumnReferenceExpression; + if (column == null || column.Name != Name) { sqlText.Append(" AS "); - sqlText.Append(SqlBaseGenerator.QuoteIdentifier(_columnName)); + sqlText.Append(SqlBaseGenerator.QuoteIdentifier(Name)); } base.WriteSql(sqlText); @@ -451,17 +376,15 @@ internal override void WriteSql(StringBuilder sqlText) internal class ScanExpression : VisitedExpression { - private string _scanString; - private EntitySetBase _target; + readonly string _scanString; + internal EntitySetBase Target { get; } public ScanExpression(string scanString, EntitySetBase target) { _scanString = scanString; - _target = target; + Target = target; } - internal EntitySetBase Target { get { return _target; } } - internal override void WriteSql(StringBuilder sqlText) { sqlText.Append(_scanString); @@ -480,55 +403,11 @@ internal class InputExpression : VisitedExpression // Either FromExpression or JoinExpression public VisitedExpression From { get; set; } - - private WhereExpression _where; - - public WhereExpression Where - { - get { return _where; } - set - { - _where = value; - } - } - - private GroupByExpression _groupBy; - - public GroupByExpression GroupBy - { - get { return _groupBy; } - set - { - _groupBy = value; - } - } - - private OrderByExpression _orderBy; - - public OrderByExpression OrderBy - { - get { return _orderBy; } - set { _orderBy = value; } - } - - private SkipExpression _skip; - - public SkipExpression Skip - { - get { return _skip; } - set { _skip = value; } - } - - private LimitExpression _limit; - - public LimitExpression Limit - { - get { return _limit; } - set - { - _limit = value; - } - } + public WhereExpression Where { get; set; } + public GroupByExpression GroupBy { get; set; } + public OrderByExpression OrderBy { get; set; } + public SkipExpression Skip { get; set; } + public LimitExpression Limit { get; set; } public InputExpression() { } @@ -540,21 +419,22 @@ public InputExpression(VisitedExpression from, string asName) internal override void WriteSql(StringBuilder sqlText) { sqlText.Append("SELECT "); - if (Distinct) sqlText.Append("DISTINCT "); - if (Projection != null) Projection.WriteSql(sqlText); + if (Distinct) + sqlText.Append("DISTINCT "); + if (Projection != null) + Projection.WriteSql(sqlText); else { if (ColumnsToProject.Count == 0) sqlText.Append("1"); // Could be arbitrary, let's pick 1 else { - bool first = true; + var first = true; foreach (var column in ColumnsToProject) { if (!first) - { sqlText.Append(", "); - } - else first = false; + else + first = false; sqlText.Append(SqlBaseGenerator.QuoteIdentifier(column.Key.Item1)); sqlText.Append("."); sqlText.Append(SqlBaseGenerator.QuoteIdentifier(column.Key.Item2)); @@ -568,38 +448,34 @@ internal override void WriteSql(StringBuilder sqlText) } sqlText.Append(" FROM "); From.WriteSql(sqlText); - if (Where != null) Where.WriteSql(sqlText); - if (GroupBy != null) GroupBy.WriteSql(sqlText); - if (OrderBy != null) OrderBy.WriteSql(sqlText); - if (Skip != null) Skip.WriteSql(sqlText); - if (Limit != null) Limit.WriteSql(sqlText); + Where?.WriteSql(sqlText); + GroupBy?.WriteSql(sqlText); + OrderBy?.WriteSql(sqlText); + Skip?.WriteSql(sqlText); + Limit?.WriteSql(sqlText); base.WriteSql(sqlText); } } internal class FromExpression : VisitedExpression { - private VisitedExpression _from; - private string _name; + readonly VisitedExpression _from; + internal string Name { get; } public FromExpression(VisitedExpression from, string name) { _from = from; - _name = name; - } - - public string Name - { - get { return _name; } + Name = name; } public bool ForceSubquery { get; set; } internal override void WriteSql(StringBuilder sqlText) { - if (_from is InputExpression) + var from = _from as InputExpression; + if (from != null) { - InputExpression input = (InputExpression)_from; + var input = from; if (!ForceSubquery && input.Projection == null && input.Where == null && input.Distinct == false && input.OrderBy == null && input.Skip == null && input.Limit == null) { @@ -617,19 +493,19 @@ internal override void WriteSql(StringBuilder sqlText) sqlText.Append("("); input.WriteSql(sqlText); sqlText.Append(") AS "); - sqlText.Append(SqlBaseGenerator.QuoteIdentifier(_name)); + sqlText.Append(SqlBaseGenerator.QuoteIdentifier(Name)); } } else { - bool wrap = !(_from is LiteralExpression || _from is ScanExpression); + var wrap = !(_from is LiteralExpression || _from is ScanExpression || _from is FunctionExpression); if (wrap) sqlText.Append("("); _from.WriteSql(sqlText); if (wrap) sqlText.Append(")"); sqlText.Append(" AS "); - sqlText.Append(SqlBaseGenerator.QuoteIdentifier(_name)); + sqlText.Append(SqlBaseGenerator.QuoteIdentifier(Name)); } base.WriteSql(sqlText); } @@ -637,64 +513,54 @@ internal override void WriteSql(StringBuilder sqlText) internal class JoinExpression : VisitedExpression { - private VisitedExpression _left; - private DbExpressionKind _joinType; - private VisitedExpression _right; - private VisitedExpression _condition; + internal VisitedExpression Left { get; set; } + internal DbExpressionKind JoinType { get; set; } + internal VisitedExpression Right { get; set; } + internal VisitedExpression Condition { get; set; } public JoinExpression() { } public JoinExpression(InputExpression left, DbExpressionKind joinType, InputExpression right, VisitedExpression condition) { - _left = left; - _joinType = joinType; - _right = right; - _condition = condition; - } - - public VisitedExpression Left { get { return _left; } set { _left = value; } } - public DbExpressionKind JoinType { get { return _joinType; } set { _joinType = value; } } - public VisitedExpression Right { get { return _right; } set { _right = value; } } - - public VisitedExpression Condition - { - get { return _condition; } - set { _condition = value; } + Left = left; + JoinType = joinType; + Right = right; + Condition = condition; } internal override void WriteSql(StringBuilder sqlText) { - _left.WriteSql(sqlText); - switch (_joinType) + Left.WriteSql(sqlText); + switch (JoinType) { - case DbExpressionKind.InnerJoin: - sqlText.Append(" INNER JOIN "); - break; - case DbExpressionKind.LeftOuterJoin: - sqlText.Append(" LEFT OUTER JOIN "); - break; - case DbExpressionKind.FullOuterJoin: - sqlText.Append(" FULL OUTER JOIN "); - break; - case DbExpressionKind.CrossJoin: - sqlText.Append(" CROSS JOIN "); - break; - case DbExpressionKind.CrossApply: - sqlText.Append(" CROSS JOIN LATERAL "); - break; - case DbExpressionKind.OuterApply: - sqlText.Append(" LEFT OUTER JOIN LATERAL "); - break; - default: - throw new NotSupportedException(); + case DbExpressionKind.InnerJoin: + sqlText.Append(" INNER JOIN "); + break; + case DbExpressionKind.LeftOuterJoin: + sqlText.Append(" LEFT OUTER JOIN "); + break; + case DbExpressionKind.FullOuterJoin: + sqlText.Append(" FULL OUTER JOIN "); + break; + case DbExpressionKind.CrossJoin: + sqlText.Append(" CROSS JOIN "); + break; + case DbExpressionKind.CrossApply: + sqlText.Append(" CROSS JOIN LATERAL "); + break; + case DbExpressionKind.OuterApply: + sqlText.Append(" LEFT OUTER JOIN LATERAL "); + break; + default: + throw new NotSupportedException(); } - _right.WriteSql(sqlText); - if (_joinType == DbExpressionKind.OuterApply) + Right.WriteSql(sqlText); + if (JoinType == DbExpressionKind.OuterApply) sqlText.Append(" ON TRUE"); - else if (_joinType != DbExpressionKind.CrossJoin && _joinType != DbExpressionKind.CrossApply) + else if (JoinType != DbExpressionKind.CrossJoin && JoinType != DbExpressionKind.CrossApply) { sqlText.Append(" ON "); - _condition.WriteSql(sqlText); + Condition.WriteSql(sqlText); } base.WriteSql(sqlText); } @@ -702,7 +568,7 @@ internal override void WriteSql(StringBuilder sqlText) internal class WhereExpression : VisitedExpression { - private VisitedExpression _where; + VisitedExpression _where; public WhereExpression(VisitedExpression where) { @@ -725,7 +591,9 @@ internal void And(VisitedExpression andAlso) internal class PropertyExpression : VisitedExpression { - private EdmMember _property; + readonly EdmMember _property; + public string Name => _property.Name; + public TypeUsage PropertyType => _property.TypeUsage; // used for inserts or updates where the column is not qualified public PropertyExpression(EdmMember property) @@ -733,10 +601,6 @@ public PropertyExpression(EdmMember property) _property = property; } - public string Name { get { return _property.Name; } } - - public TypeUsage PropertyType { get { return _property.TypeUsage; } } - internal override void WriteSql(StringBuilder sqlText) { sqlText.Append(SqlBaseGenerator.QuoteIdentifier(_property.Name)); @@ -745,16 +609,13 @@ internal override void WriteSql(StringBuilder sqlText) // override ToString since we don't want variable substitution or identifier quoting // until writing out the SQL. - public override string ToString() - { - return _property.Name; - } + public override string ToString() => Name; } internal class FunctionExpression : VisitedExpression { - private string _name; - private List _args = new List(); + readonly string _name; + readonly List _args = new List(); public FunctionExpression(string name) { @@ -792,8 +653,8 @@ internal override void WriteSql(StringBuilder sqlText) internal class CastExpression : VisitedExpression { - private VisitedExpression _value; - private string _type; + readonly VisitedExpression _value; + readonly string _type; public CastExpression(VisitedExpression value, string type) { @@ -812,7 +673,7 @@ internal override void WriteSql(StringBuilder sqlText) internal class GroupByExpression : VisitedExpression { - private bool _requiresGroupSeperator; + bool _requiresGroupSeperator; public void AppendGroupingKey(VisitedExpression key) { @@ -832,26 +693,24 @@ internal override void WriteSql(StringBuilder sqlText) internal class LimitExpression : VisitedExpression { - private VisitedExpression _arg; - - public VisitedExpression Arg { get { return _arg; } set { _arg = value; } } + internal VisitedExpression Arg { get; set; } public LimitExpression(VisitedExpression arg) { - _arg = arg; + Arg = arg; } internal override void WriteSql(StringBuilder sqlText) { sqlText.Append(" LIMIT "); - _arg.WriteSql(sqlText); + Arg.WriteSql(sqlText); base.WriteSql(sqlText); } } internal class SkipExpression : VisitedExpression { - private VisitedExpression _arg; + readonly VisitedExpression _arg; public SkipExpression(VisitedExpression arg) { @@ -868,19 +727,13 @@ internal override void WriteSql(StringBuilder sqlText) internal class Operator { - private string op; - private int leftPrecedence; - private int rightPrecedence; - private int newPrecedence; // Since PostgreSQL 9.5, the operator precedence was changed - private UnaryTypes unaryType; - private bool rightAssoc; - - public string Op { get { return op; } } - public int LeftPrecedence { get { return leftPrecedence; } } - public int RightPrecedence { get { return rightPrecedence; } } - public int NewPrecedence { get { return newPrecedence; } } - public UnaryTypes UnaryType { get { return unaryType; } } - public bool RightAssoc { get { return rightAssoc; } } + internal string Op { get; } + internal int LeftPrecedence { get; } + internal int RightPrecedence { get; } + // Since PostgreSQL 9.5, the operator precedence was changed + internal int NewPrecedence { get; } + internal UnaryTypes UnaryType { get; } + internal bool RightAssoc { get; } internal enum UnaryTypes { Binary, @@ -888,32 +741,32 @@ internal enum UnaryTypes { Postfix } - private Operator(string op, int precedence, int newPrecedence) + Operator(string op, int precedence, int newPrecedence) { - this.op = ' ' + op + ' '; - this.leftPrecedence = precedence; - this.rightPrecedence = precedence; - this.newPrecedence = newPrecedence; - this.unaryType = UnaryTypes.Binary; + Op = ' ' + op + ' '; + LeftPrecedence = precedence; + RightPrecedence = precedence; + NewPrecedence = newPrecedence; + UnaryType = UnaryTypes.Binary; } - private Operator(string op, int leftPrecedence, int rightPrecedence, int newPrecedence) + Operator(string op, int leftPrecedence, int rightPrecedence, int newPrecedence) { - this.op = ' ' + op + ' '; - this.leftPrecedence = leftPrecedence; - this.rightPrecedence = rightPrecedence; - this.newPrecedence = newPrecedence; - this.unaryType = UnaryTypes.Binary; + Op = ' ' + op + ' '; + LeftPrecedence = leftPrecedence; + RightPrecedence = rightPrecedence; + NewPrecedence = newPrecedence; + UnaryType = UnaryTypes.Binary; } - private Operator(string op, int precedence, int newPrecedence, UnaryTypes unaryType, bool rightAssoc) + Operator(string op, int precedence, int newPrecedence, UnaryTypes unaryType, bool rightAssoc) { - this.op = unaryType == UnaryTypes.Binary ? ' ' + op + ' ' : unaryType == UnaryTypes.Prefix ? op + ' ' : ' ' + op; - this.leftPrecedence = precedence; - this.rightPrecedence = precedence; - this.newPrecedence = newPrecedence; - this.unaryType = unaryType; - this.rightAssoc = rightAssoc; + Op = unaryType == UnaryTypes.Binary ? ' ' + op + ' ' : unaryType == UnaryTypes.Prefix ? op + ' ' : ' ' + op; + LeftPrecedence = precedence; + RightPrecedence = precedence; + NewPrecedence = newPrecedence; + UnaryType = unaryType; + RightAssoc = rightAssoc; } /* @@ -952,7 +805,7 @@ private Operator(string op, int precedence, int newPrecedence, UnaryTypes unaryT public static readonly Operator NotLike = new Operator("NOT LIKE", 3, 6, 6); public static readonly Operator LessThan = new Operator("<", 5, 5); public static readonly Operator GreaterThan = new Operator(">", 5, 5); - public static readonly new Operator Equals = new Operator("=", 4, 5, UnaryTypes.Binary, true); + public new static readonly Operator Equals = new Operator("=", 4, 5, UnaryTypes.Binary, true); public static readonly Operator Not = new Operator("NOT", 3, 3, UnaryTypes.Prefix, true); public static readonly Operator And = new Operator("AND", 2, 2); public static readonly Operator Or = new Operator("OR", 1, 1); @@ -963,6 +816,7 @@ private Operator(string op, int precedence, int newPrecedence, UnaryTypes unaryT public static readonly Operator QueryNegate = new Operator("!!", 10, 8, UnaryTypes.Prefix, true); public static readonly Operator QueryContains = new Operator("@>", 10, 8); public static readonly Operator QueryIsContained = new Operator("<@", 10, 8); + public static readonly Operator RegexMatch = new Operator("~", 10, 8); public static readonly Dictionary NegateDict; @@ -970,61 +824,53 @@ static Operator() { NegateDict = new Dictionary() { - {IsNull, IsNotNull}, - {IsNotNull, IsNull}, - {LessThanOrEquals, GreaterThan}, - {GreaterThanOrEquals, LessThan}, - {NotEquals, Equals}, - {In, NotIn}, - {NotIn, In}, - {Like, NotLike}, - {NotLike, Like}, - {LessThan, GreaterThanOrEquals}, - {GreaterThan, LessThanOrEquals}, - {Equals, NotEquals} + { IsNull, IsNotNull }, + { IsNotNull, IsNull }, + { LessThanOrEquals, GreaterThan }, + { GreaterThanOrEquals, LessThan }, + { NotEquals, Equals }, + { In, NotIn }, + { NotIn, In }, + { Like, NotLike }, + { NotLike, Like }, + { LessThan, GreaterThanOrEquals }, + { GreaterThan, LessThanOrEquals }, + { Equals, NotEquals } }; } } internal class OperatorExpression : VisitedExpression { - private Operator op; - private bool useNewPrecedences; - private VisitedExpression left; - private VisitedExpression right; + Operator _op; + readonly bool _useNewPrecedences; + readonly VisitedExpression _left; + readonly VisitedExpression _right; - private OperatorExpression(Operator op, bool useNewPrecedences, VisitedExpression left, VisitedExpression right) + OperatorExpression(Operator op, bool useNewPrecedences, [CanBeNull] VisitedExpression left, [CanBeNull] VisitedExpression right) { - this.op = op; - this.useNewPrecedences = useNewPrecedences; - this.left = left; - this.right = right; + _op = op; + _useNewPrecedences = useNewPrecedences; + _left = left; + _right = right; } public static OperatorExpression Build(Operator op, bool useNewPrecedences, VisitedExpression left, VisitedExpression right) { if (op.UnaryType == Operator.UnaryTypes.Binary) - { return new OperatorExpression(op, useNewPrecedences, left, right); - } - else - { - throw new InvalidOperationException("Unary operator with two operands"); - } + throw new InvalidOperationException("Unary operator with two operands"); } public static OperatorExpression Build(Operator op, bool useNewPrecedences, VisitedExpression exp) { - if (op.UnaryType == Operator.UnaryTypes.Prefix) + switch (op.UnaryType) { + case Operator.UnaryTypes.Prefix: return new OperatorExpression(op, useNewPrecedences, null, exp); - } - else if (op.UnaryType == Operator.UnaryTypes.Postfix) - { + case Operator.UnaryTypes.Postfix: return new OperatorExpression(op, useNewPrecedences, exp, null); - } - else - { + default: throw new InvalidOperationException("Binary operator with one operand"); } } @@ -1036,23 +882,21 @@ public static OperatorExpression Build(Operator op, bool useNewPrecedences, Visi /// public static VisitedExpression Negate(VisitedExpression exp, bool useNewPrecedences) { - OperatorExpression expOp = exp as OperatorExpression; + var expOp = exp as OperatorExpression; if (expOp != null) { - Operator op = expOp.op; - Operator newOp = null; + var op = expOp._op; + Operator newOp; if (Operator.NegateDict.TryGetValue(op, out newOp)) { - expOp.op = newOp; + expOp._op = newOp; return expOp; } - if (expOp.op == Operator.Not) - { - return expOp.right; - } + if (expOp._op == Operator.Not) + return expOp._right; } - return OperatorExpression.Build(Operator.Not, useNewPrecedences, exp); + return Build(Operator.Not, useNewPrecedences, exp); } internal override void WriteSql(StringBuilder sqlText) @@ -1060,60 +904,60 @@ internal override void WriteSql(StringBuilder sqlText) WriteSql(sqlText, null); } - private void WriteSql(StringBuilder sqlText, OperatorExpression rightParent) + void WriteSql(StringBuilder sqlText, [CanBeNull] OperatorExpression rightParent) { - OperatorExpression leftOp = left as OperatorExpression; - OperatorExpression rightOp = right as OperatorExpression; + var leftOp = _left as OperatorExpression; + var rightOp = _right as OperatorExpression; bool wrapLeft, wrapRight; - if (!useNewPrecedences) + if (!_useNewPrecedences) { - wrapLeft = leftOp != null && (op.RightAssoc ? leftOp.op.RightPrecedence <= op.LeftPrecedence : leftOp.op.RightPrecedence < op.LeftPrecedence); - wrapRight = rightOp != null && (!op.RightAssoc ? rightOp.op.LeftPrecedence <= op.RightPrecedence : rightOp.op.LeftPrecedence < op.RightPrecedence); + wrapLeft = leftOp != null && (_op.RightAssoc ? leftOp._op.RightPrecedence <= _op.LeftPrecedence : leftOp._op.RightPrecedence < _op.LeftPrecedence); + wrapRight = rightOp != null && (!_op.RightAssoc ? rightOp._op.LeftPrecedence <= _op.RightPrecedence : rightOp._op.LeftPrecedence < _op.RightPrecedence); } else { - wrapLeft = leftOp != null && (op.RightAssoc ? leftOp.op.NewPrecedence <= op.NewPrecedence : leftOp.op.NewPrecedence < op.NewPrecedence); - wrapRight = rightOp != null && (!op.RightAssoc ? rightOp.op.NewPrecedence <= op.NewPrecedence : rightOp.op.NewPrecedence < op.NewPrecedence); + wrapLeft = leftOp != null && (_op.RightAssoc ? leftOp._op.NewPrecedence <= _op.NewPrecedence : leftOp._op.NewPrecedence < _op.NewPrecedence); + wrapRight = rightOp != null && (!_op.RightAssoc ? rightOp._op.NewPrecedence <= _op.NewPrecedence : rightOp._op.NewPrecedence < _op.NewPrecedence); } // Avoid parentheses for prefix operators if possible, // e.g. BitwiseNot: (a & (~ b)) & c is written as a & ~ b & c // but (a + (~ b)) + c must be written as a + (~ b) + c - if (!useNewPrecedences) + if (!_useNewPrecedences) { - if (wrapRight && rightOp.left == null && (rightParent == null || (!rightParent.op.RightAssoc ? rightOp.op.RightPrecedence >= rightParent.op.LeftPrecedence : rightOp.op.RightPrecedence > rightParent.op.LeftPrecedence))) + if (wrapRight && rightOp._left == null && (rightParent == null || (!rightParent._op.RightAssoc ? rightOp._op.RightPrecedence >= rightParent._op.LeftPrecedence : rightOp._op.RightPrecedence > rightParent._op.LeftPrecedence))) wrapRight = false; } else { - if (wrapRight && rightOp.left == null && (rightParent == null || (!rightParent.op.RightAssoc ? rightOp.op.NewPrecedence >= rightParent.op.NewPrecedence : rightOp.op.NewPrecedence > rightParent.op.NewPrecedence))) + if (wrapRight && rightOp._left == null && (rightParent == null || (!rightParent._op.RightAssoc ? rightOp._op.NewPrecedence >= rightParent._op.NewPrecedence : rightOp._op.NewPrecedence > rightParent._op.NewPrecedence))) wrapRight = false; } - if (left != null) + if (_left != null) { if (wrapLeft) sqlText.Append("("); if (leftOp != null && !wrapLeft) leftOp.WriteSql(sqlText, this); else - left.WriteSql(sqlText); + _left.WriteSql(sqlText); if (wrapLeft) sqlText.Append(")"); } - sqlText.Append(op.Op); + sqlText.Append(_op.Op); - if (right != null) + if (_right != null) { if (wrapRight) sqlText.Append("("); if (rightOp != null && !wrapRight) rightOp.WriteSql(sqlText, rightParent); else - right.WriteSql(sqlText); + _right.WriteSql(sqlText); if (wrapRight) sqlText.Append(")"); } @@ -1124,7 +968,7 @@ private void WriteSql(StringBuilder sqlText, OperatorExpression rightParent) internal class ConstantListExpression : VisitedExpression { - private IEnumerable _list; + readonly IEnumerable _list; public ConstantListExpression(IEnumerable list) { @@ -1134,7 +978,7 @@ public ConstantListExpression(IEnumerable list) internal override void WriteSql(StringBuilder sqlText) { sqlText.Append("("); - bool first = true; + var first = true; foreach (var constant in _list) { if (!first) @@ -1149,12 +993,16 @@ internal override void WriteSql(StringBuilder sqlText) internal class CombinedProjectionExpression : VisitedExpression { - private List _list; - private string _setOperator; + readonly List _list; + readonly string _setOperator; public CombinedProjectionExpression(DbExpressionKind setOperator, List list) { - _setOperator = setOperator == DbExpressionKind.UnionAll ? "UNION ALL" : setOperator == DbExpressionKind.Except ? "EXCEPT" : "INTERSECT"; + _setOperator = setOperator == DbExpressionKind.UnionAll + ? "UNION ALL" + : setOperator == DbExpressionKind.Except + ? "EXCEPT" + : "INTERSECT"; _list = list; } @@ -1163,9 +1011,7 @@ internal override void WriteSql(StringBuilder sqlText) for (var i = 0; i < _list.Count; i++) { if (i != 0) - { sqlText.Append(' ').Append(_setOperator).Append(' '); - } sqlText.Append('('); _list[i].WriteSql(sqlText); sqlText.Append(')'); @@ -1177,7 +1023,7 @@ internal override void WriteSql(StringBuilder sqlText) internal class ExistsExpression : VisitedExpression { - private VisitedExpression _argument; + readonly VisitedExpression _argument; public ExistsExpression(VisitedExpression argument) { @@ -1195,17 +1041,14 @@ internal override void WriteSql(StringBuilder sqlText) class OrderByExpression : VisitedExpression { - private bool _requiresOrderSeperator; + bool _requiresOrderSeperator; public void AppendSort(VisitedExpression sort, bool ascending) { if (_requiresOrderSeperator) Append(","); Append(sort); - if (ascending) - Append(" ASC "); - else - Append(" DESC "); + Append(ascending ? " ASC " : " DESC "); _requiresOrderSeperator = true; } diff --git a/EF6.PG/content/App.config.install.xdt b/EF6.PG/content/App.config.install.xdt new file mode 100644 index 0000000..ace92ed --- /dev/null +++ b/EF6.PG/content/App.config.install.xdt @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/EF6.PG/content/Web.config.install.xdt b/EF6.PG/content/Web.config.install.xdt new file mode 100644 index 0000000..ace92ed --- /dev/null +++ b/EF6.PG/content/Web.config.install.xdt @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/EF6.PG/content/net45/App.config.transform b/EF6.PG/content/net45/App.config.transform new file mode 100644 index 0000000..1dad58e --- /dev/null +++ b/EF6.PG/content/net45/App.config.transform @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/EF6.PG/content/net45/Web.config.transform b/EF6.PG/content/net45/Web.config.transform new file mode 100644 index 0000000..1dad58e --- /dev/null +++ b/EF6.PG/content/net45/Web.config.transform @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/EF6.PG/content/net461/App.config.transform b/EF6.PG/content/net461/App.config.transform new file mode 100644 index 0000000..0899448 --- /dev/null +++ b/EF6.PG/content/net461/App.config.transform @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/EF6.PG/content/net461/Web.config.transform b/EF6.PG/content/net461/Web.config.transform new file mode 100644 index 0000000..0899448 --- /dev/null +++ b/EF6.PG/content/net461/Web.config.transform @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/EF6.PG/postgresql.png b/EF6.PG/postgresql.png new file mode 100644 index 0000000..3a21b19 Binary files /dev/null and b/EF6.PG/postgresql.png differ diff --git a/EntityFramework6.Npgsql.sln b/EntityFramework6.Npgsql.sln index f4ccf6f..d04e93d 100644 --- a/EntityFramework6.Npgsql.sln +++ b/EntityFramework6.Npgsql.sln @@ -1,23 +1,17 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2035 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4A5A60DD-41B6-40BF-B677-227A921ECCC8}" ProjectSection(SolutionItems) = preProject - CommonAssemblyInfo.cs = CommonAssemblyInfo.cs + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets Npgsql.snk = Npgsql.snk EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EF6.PG", "EF6.PG\EF6.PG.csproj", "{3EC85CBA-5B79-11E3-8104-0022198AB089}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{ED612DB1-AB32-4603-95E7-891BACA71C39}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework6.Npgsql", "src\EntityFramework6.Npgsql\EntityFramework6.Npgsql.csproj", "{3EC85CBA-5B79-11E3-8104-0022198AB089}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework5.Npgsql", "src\EntityFramework5.Npgsql\EntityFramework5.Npgsql.csproj", "{100998C4-5B85-11E3-911C-0022198AB089}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework6.Npgsql.Tests", "test\EntityFramework6.Npgsql.Tests\EntityFramework6.Npgsql.Tests.csproj", "{4A0A42DE-C8B8-11E4-8EC9-005056A163A4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EF6.PG.Tests", "EF6.PG.Tests\EF6.PG.Tests.csproj", "{4A0A42DE-C8B8-11E4-8EC9-005056A163A4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -29,10 +23,6 @@ Global {3EC85CBA-5B79-11E3-8104-0022198AB089}.Debug|Any CPU.Build.0 = Debug|Any CPU {3EC85CBA-5B79-11E3-8104-0022198AB089}.Release|Any CPU.ActiveCfg = Release|Any CPU {3EC85CBA-5B79-11E3-8104-0022198AB089}.Release|Any CPU.Build.0 = Release|Any CPU - {100998C4-5B85-11E3-911C-0022198AB089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {100998C4-5B85-11E3-911C-0022198AB089}.Debug|Any CPU.Build.0 = Debug|Any CPU - {100998C4-5B85-11E3-911C-0022198AB089}.Release|Any CPU.ActiveCfg = Release|Any CPU - {100998C4-5B85-11E3-911C-0022198AB089}.Release|Any CPU.Build.0 = Release|Any CPU {4A0A42DE-C8B8-11E4-8EC9-005056A163A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4A0A42DE-C8B8-11E4-8EC9-005056A163A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A0A42DE-C8B8-11E4-8EC9-005056A163A4}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -41,10 +31,8 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {3EC85CBA-5B79-11E3-8104-0022198AB089} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050} - {100998C4-5B85-11E3-911C-0022198AB089} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050} - {4A0A42DE-C8B8-11E4-8EC9-005056A163A4} = {ED612DB1-AB32-4603-95E7-891BACA71C39} + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {84B4C9EA-ED4F-4E87-8809-297958315622} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = Npgsql.csproj diff --git a/EntityFramework6.Npgsql.sln.DotSettings b/EntityFramework6.Npgsql.sln.DotSettings index db04052..5dfab15 100644 --- a/EntityFramework6.Npgsql.sln.DotSettings +++ b/EntityFramework6.Npgsql.sln.DotSettings @@ -2,6 +2,7 @@ True Implicit False + NEVER False True GSS @@ -53,6 +54,12 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True True + True True True \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..edaca74 --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +Copyright (c) 2002-2018, Npgsql + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose, without fee, and without a written agreement +is hereby granted, provided that the above copyright notice and this +paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL NPGSQL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF +Npgsql HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +NPGSQL SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND Npgsql +HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, +OR MODIFICATIONS. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 0a45364..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2002-2016, The Npgsql Development Team - -Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. - -IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a00fdb6 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Entity Framework 6 Provider for PostgreSQL + +This is the Npgsql PostgreSQL provider for Entity Framework 6; note that this is the old, non-core version of EF. For the EF Core provider, see https://github.com/npgsql/efcore.pg. + +Like non-core Entity Framework 6, this provider is no longer being maintained. It is unlikely that any enhancements or bug fixes will be worked upon, unless they're absolutely critical. \ No newline at end of file diff --git a/bump.sh b/bump.sh deleted file mode 100644 index 3a46822..0000000 --- a/bump.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -if [ "$#" -ne 1 ]; then - echo "usage: bump.sh " - exit 1 -fi - -v=$1 -if [[ $v == *"-" ]]; then - echo "Version must not end with -" - exit 1 -fi - -echo "echo ##teamcity[buildNumber '$v-%1']" > teamcity_set_version.cmd - -if [[ $v == *"-"* ]]; then - # Prerelease version - - without_prerelease=`echo $v | cut -d- -f1` - - sed -i 's/^\(\s*\)[^<]*<\/version>/\1$version$<\/version>/' src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.nuspec - sed -i 's/^\(\s*\)[^<]*<\/version>/\1$version$<\/version>/' src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.nuspec - - sed -i 's/AssemblyVersion("[^"]*")/AssemblyVersion("'$without_prerelease'")/' src/CommonAssemblyInfo.cs - sed -i 's/AssemblyFileVersion("[^"]*")/AssemblyFileVersion("'$without_prerelease'")/' src/CommonAssemblyInfo.cs - sed -i 's/AssemblyInformationalVersion("[^"]*")/AssemblyInformationalVersion("'$v'")/' src/CommonAssemblyInfo.cs -else - # Release version - - sed -i 's/^\(\s*\)[^<]*<\/version>/\1'$v'<\/version>/' src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.nuspec - sed -i 's/^\(\s*\)[^<]*<\/version>/\1'$v'<\/version>/' src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.nuspec - - sed -i 's/AssemblyVersion("[^"]*")/AssemblyVersion("'$v'")/' src/CommonAssemblyInfo.cs - sed -i 's/AssemblyFileVersion("[^"]*")/AssemblyFileVersion("'$v'")/' src/CommonAssemblyInfo.cs - sed -i 's/AssemblyInformationalVersion("[^"]*")/AssemblyInformationalVersion("'$v'")/' src/CommonAssemblyInfo.cs -fi - -git add teamcity_set_version.cmd -git add src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.nuspec -git add src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.nuspec -git add src/CommonAssemblyInfo.cs - -git commit -m "Bump version to $v" diff --git a/src/CommonAssemblyInfo.cs b/src/CommonAssemblyInfo.cs deleted file mode 100644 index 1a7f45e..0000000 --- a/src/CommonAssemblyInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Security; -using System.Reflection; -using System.Resources; - -// Contains assembly attributes shared by all Npgsql projects - -[assembly: CLSCompliant(false)] -[assembly: AllowPartiallyTrustedCallers()] -[assembly: SecurityRules(SecurityRuleSet.Level1)] -[assembly: AssemblyCompany("Npgsql Development Team")] -[assembly: AssemblyProduct("Npgsql")] -[assembly: AssemblyCopyright("Copyright © 2002 - 2016 Npgsql Development Team")] -[assembly: AssemblyTrademark("")] -[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.MainAssembly)] - -// The following version attributes get rewritten by GitVersion as part of the build -[assembly: AssemblyVersion("3.1.0")] -[assembly: AssemblyFileVersion("3.1.0")] -[assembly: AssemblyInformationalVersion("3.1.0")] diff --git a/src/EntityFramework5.Npgsql/App.config b/src/EntityFramework5.Npgsql/App.config deleted file mode 100644 index 0c119f7..0000000 --- a/src/EntityFramework5.Npgsql/App.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - -
- - - - - - - - - - - - \ No newline at end of file diff --git a/src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.csproj b/src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.csproj deleted file mode 100644 index df94040..0000000 --- a/src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.csproj +++ /dev/null @@ -1,83 +0,0 @@ - - - - Debug - AnyCPU - {100998C4-5B85-11E3-911C-0022198AB089} - Library - Properties - EntityFramework5.Npgsql - Npgsql - 512 - true - ..\..\Npgsql.snk - ..\ - true - - v4.5 - 5 - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET45 - prompt - 4 - bin\Debug\EntityFramework5.Npgsql.xml - - - pdbonly - true - bin\Release\ - TRACE;NET45 - prompt - 4 - bin\Release\EntityFramework5.Npgsql.xml - - - - ..\..\packages\Npgsql.3.1.2\lib\net45\Npgsql.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - Code - - - - - - - - \ No newline at end of file diff --git a/src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.nuspec b/src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.nuspec deleted file mode 100644 index 07f54f7..0000000 --- a/src/EntityFramework5.Npgsql/EntityFramework5.Npgsql.nuspec +++ /dev/null @@ -1,29 +0,0 @@ - - - - EntityFramework5.Npgsql - Npgsql for Entity Framework 5 - 3.1.0 - Shay Rojansky, Emil Lenngren, Francisco Figueiredo Jr., Kenji Uno, Jon Asher, John Cooley, Federico Di Gregorio, Jon Hanna, Chris Morgan, Dave Page, Glen Parker, Brar Piening, Hiroshi Saito - Shay Rojansky, Emil Lenngren, Francisco Figueiredo Jr., Kenji Uno - https://github.com/npgsql/EntityFramework6.Npgsql/blob/dev/LICENSE.txt - http://www.npgsql.org - http://www.npgsql.org/img/postgresql.gif - Copyright 2002 - 2016 Npgsql Development Team - false - PostgreSQL provider for Entity Framework 5 - PostgreSQL provider for Entity Framework 5 - en-US - npgsql postgresql postgres data database entity framework ef orm - - - - - - - - - - - - diff --git a/src/EntityFramework5.Npgsql/packages.config b/src/EntityFramework5.Npgsql/packages.config deleted file mode 100644 index a55205e..0000000 --- a/src/EntityFramework5.Npgsql/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.csproj b/src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.csproj deleted file mode 100644 index 1d7fb7c..0000000 --- a/src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.csproj +++ /dev/null @@ -1,94 +0,0 @@ - - - - Debug - AnyCPU - {3EC85CBA-5B79-11E3-8104-0022198AB089} - Library - Properties - EntityFramework6.Npgsql - Npgsql - 512 - true - ..\..\Npgsql.snk - ..\ - true - - v4.5 - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET45;ENTITIES6 - prompt - 4 - bin\Debug\EntityFramework6.Npgsql.xml - - - pdbonly - true - bin\Release\ - TRACE;NET45;ENTITIES6 - prompt - 4 - bin\Release\EntityFramework6.Npgsql.xml - - - - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll - True - - - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - True - - - ..\..\packages\Npgsql.3.1.2\lib\net45\Npgsql.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - Code - - - - - - - - \ No newline at end of file diff --git a/src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.nuspec b/src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.nuspec deleted file mode 100644 index a3aaaf4..0000000 --- a/src/EntityFramework6.Npgsql/EntityFramework6.Npgsql.nuspec +++ /dev/null @@ -1,30 +0,0 @@ - - - - EntityFramework6.Npgsql - Npgsql for Entity Framework 6 - 3.1.0 - Shay Rojansky, Emil Lenngren, Francisco Figueiredo Jr., Kenji Uno, Jon Asher, John Cooley, Federico Di Gregorio, Jon Hanna, Chris Morgan, Dave Page, Glen Parker, Brar Piening, Hiroshi Saito - Shay Rojansky, Emil Lenngren, Francisco Figueiredo Jr., Kenji Uno - https://github.com/npgsql/EntityFramework6.Npgsql/blob/dev/LICENSE.txt - http://www.npgsql.org - http://www.npgsql.org/img/postgresql.gif - Copyright 2002 - 2016 Npgsql Development Team - false - PostgreSQL provider for Entity Framework 6 - PostgreSQL provider for Entity Framework 6 - en-US - npgsql postgresql postgres data database entity framework ef orm - - - - - - - - - - - - - diff --git a/src/EntityFramework6.Npgsql/NpgsqlConnectionFactory.cs b/src/EntityFramework6.Npgsql/NpgsqlConnectionFactory.cs deleted file mode 100644 index 926d39c..0000000 --- a/src/EntityFramework6.Npgsql/NpgsqlConnectionFactory.cs +++ /dev/null @@ -1,44 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System.Data.Common; -using System.Data.Entity.Infrastructure; - -namespace Npgsql -{ - /// - /// Instances of this class are used to create DbConnection objects for Postgresql - /// - public class NpgsqlConnectionFactory : IDbConnectionFactory - { - /// - /// Creates a connection for Postgresql for the given connection string. - /// - /// The connection string. - /// An initialized DbConnection. - public DbConnection CreateConnection(string nameOrConnectionString) - { - return new NpgsqlConnection(nameOrConnectionString); - } - } -} diff --git a/src/EntityFramework6.Npgsql/NpgsqlProviderManifest.cs b/src/EntityFramework6.Npgsql/NpgsqlProviderManifest.cs deleted file mode 100644 index b990f72..0000000 --- a/src/EntityFramework6.Npgsql/NpgsqlProviderManifest.cs +++ /dev/null @@ -1,442 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; -#if ENTITIES6 -using System.Data.Entity; -using System.Data.Entity.Core.Common; -using System.Data.Entity.Core.Metadata.Edm; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -#else -using System.Data.Common; -using System.Data.Metadata.Edm; -#endif -using System.Xml; -using System.Data; -using NpgsqlTypes; - -namespace Npgsql -{ - internal class NpgsqlProviderManifest : DbXmlEnabledProviderManifest - { - private Version _version; - - public Version Version { get { return _version; } } - - public NpgsqlProviderManifest(string serverVersion) - : base(CreateXmlReaderForResource("Npgsql.NpgsqlProviderManifest.Manifest.xml")) - { - if (!Version.TryParse(serverVersion, out _version)) - { - _version = new Version(9, 5); - } - } - - protected override XmlReader GetDbInformation(string informationType) - { - XmlReader xmlReader = null; - - if (informationType == StoreSchemaDefinition) - { - xmlReader = CreateXmlReaderForResource("Npgsql.NpgsqlSchema.ssdl"); - } -#if !NET40 - else if (informationType == StoreSchemaDefinitionVersion3) - { - xmlReader = CreateXmlReaderForResource("Npgsql.NpgsqlSchemaV3.ssdl"); - } -#endif - else if (informationType == StoreSchemaMapping) - { - xmlReader = CreateXmlReaderForResource("Npgsql.NpgsqlSchema.msl"); - } - - if (xmlReader == null) - throw new ArgumentOutOfRangeException("informationType"); - - return xmlReader; - } - - private const string MaxLengthFacet = "MaxLength"; - private const string ScaleFacet = "Scale"; - private const string PrecisionFacet = "Precision"; - private const string FixedLengthFacet = "FixedLength"; - - internal static NpgsqlDbType GetNpgsqlDbType(PrimitiveTypeKind _primitiveType) - { - switch (_primitiveType) - { - case PrimitiveTypeKind.Binary: - return NpgsqlDbType.Bytea; - case PrimitiveTypeKind.Boolean: - return NpgsqlDbType.Boolean; - case PrimitiveTypeKind.Byte: - case PrimitiveTypeKind.SByte: - case PrimitiveTypeKind.Int16: - return NpgsqlDbType.Smallint; - case PrimitiveTypeKind.DateTime: - return NpgsqlDbType.Timestamp; - case PrimitiveTypeKind.DateTimeOffset: - return NpgsqlDbType.TimestampTZ; - case PrimitiveTypeKind.Decimal: - return NpgsqlDbType.Numeric; - case PrimitiveTypeKind.Double: - return NpgsqlDbType.Double; - case PrimitiveTypeKind.Int32: - return NpgsqlDbType.Integer; - case PrimitiveTypeKind.Int64: - return NpgsqlDbType.Bigint; - case PrimitiveTypeKind.Single: - return NpgsqlDbType.Real; - case PrimitiveTypeKind.Time: - return NpgsqlDbType.Interval; - case PrimitiveTypeKind.Guid: - return NpgsqlDbType.Uuid; - case PrimitiveTypeKind.String: - // Send strings as unknowns to be compatible with other datatypes than text - return NpgsqlDbType.Unknown; - default: - return NpgsqlDbType.Unknown; - } - } - - public override TypeUsage GetEdmType(TypeUsage storeType) - { - if (storeType == null) - throw new ArgumentNullException("storeType"); - - string storeTypeName = storeType.EdmType.Name; - PrimitiveType primitiveType = StoreTypeNameToEdmPrimitiveType[storeTypeName]; - // TODO: come up with way to determin if unicode is used - bool isUnicode = true; - Facet facet; - - switch (storeTypeName) - { - case "bool": - case "int2": - case "int4": - case "int8": - case "float4": - case "float8": - case "uuid": - return TypeUsage.CreateDefaultTypeUsage(primitiveType); - case "numeric": - { - byte scale; - byte precision; - if (storeType.Facets.TryGetValue(ScaleFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - scale = (byte)facet.Value; - if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - precision = (byte)facet.Value; - return TypeUsage.CreateDecimalTypeUsage(primitiveType, precision, scale); - } - } - return TypeUsage.CreateDecimalTypeUsage(primitiveType); - } - case "bpchar": - if (storeType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, true, (int)facet.Value); - else - return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, true); - case "varchar": - if (storeType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, false, (int)facet.Value); - else - return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, false); - case "text": - case "xml": - return TypeUsage.CreateStringTypeUsage(primitiveType, isUnicode, false); - case "timestamp": - // TODO: make sure the arguments are correct here - if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateDateTimeTypeUsage(primitiveType, (byte)facet.Value); - } - else - { - return TypeUsage.CreateDateTimeTypeUsage(primitiveType, null); - } - case "date": - return TypeUsage.CreateDateTimeTypeUsage(primitiveType, 0); - case "timestamptz": - if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateDateTimeOffsetTypeUsage(primitiveType, (byte)facet.Value); - } - else - { - return TypeUsage.CreateDateTimeOffsetTypeUsage(primitiveType, null); - } - case "time": - case "interval": - if (storeType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateTimeTypeUsage(primitiveType, (byte)facet.Value); - } - else - { - return TypeUsage.CreateTimeTypeUsage(primitiveType, null); - } - case "bytea": - { - if (storeType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateBinaryTypeUsage(primitiveType, false, (int)facet.Value); - } - return TypeUsage.CreateBinaryTypeUsage(primitiveType, false); - } - case "rowversion": - { - return TypeUsage.CreateBinaryTypeUsage(primitiveType, true, 8); - } - //TypeUsage.CreateBinaryTypeUsage - //TypeUsage.CreateDateTimeTypeUsage - //TypeUsage.CreateDecimalTypeUsage - //TypeUsage.CreateStringTypeUsage - } - throw new NotSupportedException("Not supported store type: " + storeTypeName); - } - - public override TypeUsage GetStoreType(TypeUsage edmType) - { - if (edmType == null) - throw new ArgumentNullException("edmType"); - - PrimitiveType primitiveType = edmType.EdmType as PrimitiveType; - if (primitiveType == null) - throw new ArgumentException("Store does not support specified edm type"); - - // TODO: come up with way to determin if unicode is used - bool isUnicode = true; - Facet facet; - - switch (primitiveType.PrimitiveTypeKind) - { - case PrimitiveTypeKind.Boolean: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["bool"]); - case PrimitiveTypeKind.Int16: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int2"]); - case PrimitiveTypeKind.Int32: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int4"]); - case PrimitiveTypeKind.Int64: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int8"]); - case PrimitiveTypeKind.Single: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["float4"]); - case PrimitiveTypeKind.Double: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["float8"]); - case PrimitiveTypeKind.Decimal: - { - byte scale; - byte precision; - if (edmType.Facets.TryGetValue(ScaleFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - scale = (byte)facet.Value; - if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - precision = (byte)facet.Value; - return TypeUsage.CreateDecimalTypeUsage(StoreTypeNameToStorePrimitiveType["numeric"], precision, scale); - } - } - return TypeUsage.CreateDecimalTypeUsage(StoreTypeNameToStorePrimitiveType["numeric"]); - } - case PrimitiveTypeKind.String: - { - // TODO: could get character, character varying, text - if (edmType.Facets.TryGetValue(FixedLengthFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null && (bool)facet.Value) - { - PrimitiveType characterPrimitive = StoreTypeNameToStorePrimitiveType["bpchar"]; - if (edmType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateStringTypeUsage(characterPrimitive, isUnicode, true, (int)facet.Value); - } - // this may not work well - return TypeUsage.CreateStringTypeUsage(characterPrimitive, isUnicode, true); - } - if (edmType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["varchar"], isUnicode, false, (int)facet.Value); - } - // assume text since it is not fixed length and has no max length - return TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["text"], isUnicode, false); - } - case PrimitiveTypeKind.DateTime: - if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateDateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["timestamp"], (byte)facet.Value); - } - else - { - return TypeUsage.CreateDateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["timestamp"], null); - } - case PrimitiveTypeKind.DateTimeOffset: - if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateDateTimeOffsetTypeUsage(StoreTypeNameToStorePrimitiveType["timestamptz"], (byte)facet.Value); - } - else - { - return TypeUsage.CreateDateTimeOffsetTypeUsage(StoreTypeNameToStorePrimitiveType["timestamptz"], null); - } - case PrimitiveTypeKind.Time: - if (edmType.Facets.TryGetValue(PrecisionFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["interval"], (byte)facet.Value); - } - else - { - return TypeUsage.CreateTimeTypeUsage(StoreTypeNameToStorePrimitiveType["interval"], null); - } - case PrimitiveTypeKind.Binary: - { - if (edmType.Facets.TryGetValue(MaxLengthFacet, false, out facet) && - !facet.IsUnbounded && facet.Value != null) - { - return TypeUsage.CreateBinaryTypeUsage(StoreTypeNameToStorePrimitiveType["bytea"], false, (int)facet.Value); - } - return TypeUsage.CreateBinaryTypeUsage(StoreTypeNameToStorePrimitiveType["bytea"], false); - } - case PrimitiveTypeKind.Guid: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["uuid"]); - case PrimitiveTypeKind.Byte: - case PrimitiveTypeKind.SByte: - return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int2"]); - } - - throw new NotSupportedException("Not supported edm type: " + edmType); - } - - private static XmlReader CreateXmlReaderForResource(string resourceName) - { - return XmlReader.Create(System.Reflection.Assembly.GetAssembly(typeof(NpgsqlProviderManifest)).GetManifestResourceStream(resourceName)); - } - - public override bool SupportsEscapingLikeArgument(out char escapeCharacter) - { - escapeCharacter = '\\'; - return true; - } - - public override string EscapeLikeArgument(string argument) - { - return argument.Replace("\\","\\\\").Replace("%", "\\%").Replace("_", "\\_"); - } - -#if ENTITIES6 - public override bool SupportsInExpression() - { - return true; - } - - public override ReadOnlyCollection GetStoreFunctions() - { - var functions = new List(); - - functions.AddRange( - typeof(NpgsqlTextFunctions).GetTypeInfo() - .GetMethods(BindingFlags.Public | BindingFlags.Static) - .Select(x => new { Method = x, DbFunction = x.GetCustomAttribute() }) - .Where(x => x.DbFunction != null) - .Select(x => CreateFullTextEdmFunction(x.Method, x.DbFunction))); - - return functions.AsReadOnly(); - } - - private static EdmFunction CreateFullTextEdmFunction(MethodInfo method, DbFunctionAttribute dbFunctionInfo) - { - if (method == null) throw new ArgumentNullException("method"); - if (dbFunctionInfo == null) throw new ArgumentNullException("dbFunctionInfo"); - - return EdmFunction.Create( - dbFunctionInfo.FunctionName, - dbFunctionInfo.NamespaceName, - DataSpace.SSpace, - new EdmFunctionPayload - { - ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, - Schema = string.Empty, - IsBuiltIn = true, - IsAggregate = false, - IsFromProviderManifest = true, - StoreFunctionName = dbFunctionInfo.FunctionName, - IsComposable = true, - ReturnParameters = new[] - { - FunctionParameter.Create( - "ReturnType", - MapTypeToEdmType(method.ReturnType), - ParameterMode.ReturnValue) - }, - Parameters = method.GetParameters().Select( - x => FunctionParameter.Create( - x.Name, - MapTypeToEdmType(x.ParameterType), - ParameterMode.In)).ToList() - }, - new List()); - } - - private static EdmType MapTypeToEdmType(Type type) - { - var fromClrType = PrimitiveType - .GetEdmPrimitiveTypes() - .FirstOrDefault(t => t.ClrEquivalentType == type); - - if (fromClrType != null) - { - return fromClrType; - } - - if (type.IsEnum) - { - return MapTypeToEdmType(Enum.GetUnderlyingType(type)); - } - - throw new NotSupportedException( - string.Format("Unsupported type for mapping to EdmType: {0}", type.FullName)); - } -#endif - } -} diff --git a/src/EntityFramework6.Npgsql/NpgsqlServices.cs b/src/EntityFramework6.Npgsql/NpgsqlServices.cs deleted file mode 100644 index 9fe793d..0000000 --- a/src/EntityFramework6.Npgsql/NpgsqlServices.cs +++ /dev/null @@ -1,210 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; -#if ENTITIES6 -using System.Data.Entity.Core.Common; -using System.Data.Entity.Core.Common.CommandTrees; -using System.Data.Entity.Core.Metadata.Edm; -using System.Data.Entity.Migrations.Sql; -using System.Data.Entity.Infrastructure.DependencyResolution; -#else -using System.Data.Common; -using System.Data.Common.CommandTrees; -using System.Data.Metadata.Edm; -#endif -using Npgsql.SqlGenerators; -using DbConnection = System.Data.Common.DbConnection; -using DbCommand = System.Data.Common.DbCommand; - -namespace Npgsql -{ -#if ENTITIES6 - public class NpgsqlServices : DbProviderServices -#else - internal class NpgsqlServices : DbProviderServices -#endif - { - private static readonly NpgsqlServices _instance = new NpgsqlServices(); - -#if ENTITIES6 - public NpgsqlServices() - { - AddDependencyResolver(new SingletonDependencyResolver>( - () => new NpgsqlMigrationSqlGenerator(), "Npgsql")); - } -#endif - - public static NpgsqlServices Instance - { - get { return _instance; } - } - - protected override DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree) - { - return CreateCommandDefinition(CreateDbCommand(((NpgsqlProviderManifest)providerManifest).Version, commandTree)); - } - - internal DbCommand CreateDbCommand(Version serverVersion, DbCommandTree commandTree) - { - if (commandTree == null) - throw new ArgumentNullException("commandTree"); - - NpgsqlCommand command = new NpgsqlCommand(); - - foreach (KeyValuePair parameter in commandTree.Parameters) - { - NpgsqlParameter dbParameter = new NpgsqlParameter(); - dbParameter.ParameterName = parameter.Key; - dbParameter.NpgsqlDbType = NpgsqlProviderManifest.GetNpgsqlDbType(((PrimitiveType)parameter.Value.EdmType).PrimitiveTypeKind); - command.Parameters.Add(dbParameter); - } - - TranslateCommandTree(serverVersion, commandTree, command); - - return command; - } - - internal void TranslateCommandTree(Version serverVersion, DbCommandTree commandTree, DbCommand command, bool createParametersForNonSelect = true) - { - SqlBaseGenerator sqlGenerator = null; - - DbQueryCommandTree select; - DbInsertCommandTree insert; - DbUpdateCommandTree update; - DbDeleteCommandTree delete; - if ((select = commandTree as DbQueryCommandTree) != null) - { - sqlGenerator = new SqlSelectGenerator(select); - } - else if ((insert = commandTree as DbInsertCommandTree) != null) - { - sqlGenerator = new SqlInsertGenerator(insert); - } - else if ((update = commandTree as DbUpdateCommandTree) != null) - { - sqlGenerator = new SqlUpdateGenerator(update); - } - else if ((delete = commandTree as DbDeleteCommandTree) != null) - { - sqlGenerator = new SqlDeleteGenerator(delete); - } - else - { - // TODO: get a message (unsupported DbCommandTree type) - throw new ArgumentException(); - } - sqlGenerator._createParametersForConstants = select != null ? false : createParametersForNonSelect; - sqlGenerator._command = (NpgsqlCommand)command; - sqlGenerator.Version = serverVersion; - - sqlGenerator.BuildCommand(command); - } - - protected override string GetDbProviderManifestToken(DbConnection connection) - { - if (connection == null) - throw new ArgumentNullException("connection"); - string serverVersion = ""; - UsingPostgresDBConnection((NpgsqlConnection)connection, conn => - { - serverVersion = conn.ServerVersion; - }); - return serverVersion; - } - - protected override DbProviderManifest GetDbProviderManifest(string versionHint) - { - if (versionHint == null) - throw new ArgumentNullException("versionHint"); - return new NpgsqlProviderManifest(versionHint); - } - -#if ENTITIES6 - protected override bool DbDatabaseExists(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) - { - bool exists = false; - UsingPostgresDBConnection((NpgsqlConnection)connection, conn => - { - using (NpgsqlCommand command = new NpgsqlCommand("select count(*) from pg_catalog.pg_database where datname = '" + connection.Database + "';", conn)) - { - exists = Convert.ToInt32(command.ExecuteScalar()) > 0; - } - }); - return exists; - } - - protected override void DbCreateDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) - { - UsingPostgresDBConnection((NpgsqlConnection)connection, conn => - { - var sb = new StringBuilder(); - sb.Append("CREATE DATABASE \""); - sb.Append(connection.Database); - sb.Append("\""); - if (conn.EntityTemplateDatabase != null) - { - sb.Append(" TEMPLATE \""); - sb.Append(conn.EntityTemplateDatabase); - sb.Append("\""); - } - - using (NpgsqlCommand command = new NpgsqlCommand(sb.ToString(), conn)) - { - command.ExecuteNonQuery(); - } - }); - } - - protected override void DbDeleteDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) - { - UsingPostgresDBConnection((NpgsqlConnection)connection, conn => - { - //Close all connections in pool or exception "database used by another user appears" - NpgsqlConnection.ClearAllPools(); - using (NpgsqlCommand command = new NpgsqlCommand("DROP DATABASE \"" + connection.Database + "\";", conn)) - { - command.ExecuteNonQuery(); - } - }); - } -#endif - - private static void UsingPostgresDBConnection(NpgsqlConnection connection, Action action) - { - var connectionBuilder = new NpgsqlConnectionStringBuilder(connection.ConnectionString) - { - Database = connection.EntityAdminDatabase ?? "template1", - Pooling = false - }; - - using (var masterConnection = new NpgsqlConnection(connectionBuilder.ConnectionString)) - { - masterConnection.Open();//using's Dispose will close it even if exception... - action(masterConnection); - } - } - } -} diff --git a/src/EntityFramework6.Npgsql/Properties/AssemblyInfo.cs b/src/EntityFramework6.Npgsql/Properties/AssemblyInfo.cs deleted file mode 100644 index aeb1aee..0000000 --- a/src/EntityFramework6.Npgsql/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Security; -using System.Reflection; -using System.Resources; - -// Additional assembly attributes are defined in GlobalAssemblyInfo.cs - -#if ENTITIES6 -[assembly: AssemblyTitleAttribute("EntityFramework6.Npgsql")] -[assembly: AssemblyDescriptionAttribute("PostgreSQL provider for Entity Framework 6")] -#else -[assembly: AssemblyTitleAttribute("EntityFramework5.Npgsql")] -[assembly: AssemblyDescriptionAttribute("PostgreSQL provider for Entity Framework 5")] -#endif diff --git a/src/EntityFramework6.Npgsql/PublisherPolicy.config b/src/EntityFramework6.Npgsql/PublisherPolicy.config deleted file mode 100644 index bbfd05c..0000000 --- a/src/EntityFramework6.Npgsql/PublisherPolicy.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/src/EntityFramework6.Npgsql/PublisherPolicyLegacy.config b/src/EntityFramework6.Npgsql/PublisherPolicyLegacy.config deleted file mode 100644 index db8b53f..0000000 --- a/src/EntityFramework6.Npgsql/PublisherPolicyLegacy.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/src/EntityFramework6.Npgsql/Spatial/PostgisServices.cs b/src/EntityFramework6.Npgsql/Spatial/PostgisServices.cs deleted file mode 100644 index 232b683..0000000 --- a/src/EntityFramework6.Npgsql/Spatial/PostgisServices.cs +++ /dev/null @@ -1,1826 +0,0 @@ -using System; -#if ENTITIES6 -using System.Data.Entity.Spatial; -#else -using System.Data.Spatial; -#endif - -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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Double, distance); - cmd.CommandText = - "SELECT st_buffer(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = - "SELECT st_difference(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, index); - cmd.CommandText = - "SELECT st_geometryn(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, geometryCollectionWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_geomcollfromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, wellKnownBinary); - cmd.CommandText = - "SELECT st_geomfromwkb(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, wellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_geomfromwkb(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, geometryMarkup); - cmd.CommandText = - "SELECT st_geomfromgml(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, geometryMarkup); - cmd.Parameters.AddWithValue("p1", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_geomfromgml(:p1,:p2)"; - return DbSpatialServices.CreateGeometry(this, cmd.ExecuteScalar()); - } - } - - /// - /// Wrap a npgsql geometry in a DbGeometry structure. - /// - /// - /// - public override DbGeometry GeometryFromProviderValue(object providerValue) - { - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, wellKnownText); - cmd.CommandText = - "SELECT st_geomfromtext(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, wellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_geomfromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, lineWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_linefromwkb(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, lineWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_linefromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, multiLineWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_mlinefromwkb(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, multiLineWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_mlinefromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, multiPointWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_mpointfromwkb(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, multiPointWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_mpointfromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, multiPolygonWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_mpolyfromwkb(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, multiPolygonKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_mpolyfromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, pointWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_GeomFromWKB(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, pointWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_pointfromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Bytea, polygonWellKnownBinary); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_geomfromwkb(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Text, polygonWellKnownText); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, coordinateSystemId); - cmd.CommandText = - "SELECT st_geometryfromtext(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_area(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : 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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_boundary(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_centroid(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_convexhull(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_numgeometries(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new int() : 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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_endpoint(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_envelope(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_exteriorring(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_length(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : 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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_npoints(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new int?() : 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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_pointonsurface(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_startpoint(:p1)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_X(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : 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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.CommandText = - "SELECT st_y(:p1)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.IsDBNull(0) ? new double?() : 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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Integer, index); - cmd.CommandText = - "SELECT st_interiorringn(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = - "SELECT st_intersection(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.Parameters.AddWithValue("p3", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = - "SELECT st_symdifference(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = - "SELECT st_union(:p1,:p2)"; - return DbSpatialServices.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", NpgsqlTypes.NpgsqlDbType.Geometry, geometryValue.ProviderValue); - cmd.Parameters.AddWithValue("p2", NpgsqlTypes.NpgsqlDbType.Geometry, otherGeometry.ProviderValue); - cmd.CommandText = - "SELECT st_within(:p1,:p2)"; - using (var rdr = cmd.ExecuteReader()) - { - rdr.Read(); - return rdr.GetBoolean(0); - } - } - } - - private NpgsqlConnection _connection; - - /// - /// Set the provider connection - /// - /// - public void SetConnection(Npgsql.NpgsqlConnection c) - { - _connection = c; - } - } -} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/PendingProjectsNode.cs b/src/EntityFramework6.Npgsql/SqlGenerators/PendingProjectsNode.cs deleted file mode 100644 index 9f5bbc4..0000000 --- a/src/EntityFramework6.Npgsql/SqlGenerators/PendingProjectsNode.cs +++ /dev/null @@ -1,73 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Npgsql.SqlGenerators -{ - /// - /// Represents an InputExpression and what alias it will have when used in a FROM clause - /// - internal class NameAndInputExpression - { - public string AsName { get; set; } - public InputExpression Exp { get; set; } - - public NameAndInputExpression(string asName, InputExpression exp) - { - AsName = asName; - Exp = exp; - } - } - - /// - /// A tree of subqueries, used when evaluating SQL text for DbPropertyExpressions in SqlSelectGenerator. - /// See SqlSelectGenerator.Visit(DbPropertyExpression) for more information. - /// - internal class PendingProjectsNode - { - public readonly List Selects = new List(); - public PendingProjectsNode JoinParent { get; set; } - public string TopName - { - get - { - return Selects[0].AsName; - } - } - - public PendingProjectsNode(string asName, InputExpression exp) - { - Selects.Add(new NameAndInputExpression(asName, exp)); - } - public void Add(string asName, InputExpression exp) - { - Selects.Add(new NameAndInputExpression(asName, exp)); - } - - public NameAndInputExpression Last { get { return Selects[Selects.Count - 1]; } } - } -} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/SqlBaseGenerator.cs b/src/EntityFramework6.Npgsql/SqlGenerators/SqlBaseGenerator.cs deleted file mode 100644 index dd6435e..0000000 --- a/src/EntityFramework6.Npgsql/SqlGenerators/SqlBaseGenerator.cs +++ /dev/null @@ -1,1431 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Data.Common; -#if ENTITIES6 -using System.Globalization; -using System.Data.Entity.Core.Common.CommandTrees; -using System.Data.Entity.Core.Metadata.Edm; -#else -using System.Data.Common.CommandTrees; -using System.Data.Metadata.Edm; -#endif -using System.Linq; -using Npgsql; -using NpgsqlTypes; - -namespace Npgsql.SqlGenerators -{ - internal abstract class SqlBaseGenerator : DbExpressionVisitor - { - internal NpgsqlCommand _command; - internal bool _createParametersForConstants; - private Version _version; - internal Version Version { get { return _version; } set { _version = value; _useNewPrecedences = value >= new Version(9, 5); } } - private bool _useNewPrecedences; - - protected Dictionary _refToNode = new Dictionary(); - protected HashSet _currentExpressions = new HashSet(); - protected uint _aliasCounter = 0; - protected uint _parameterCount = 0; - - private static Dictionary AggregateFunctionNames = new Dictionary() - { - {"Avg","avg"}, - {"Count","count"}, - {"Min","min"}, - {"Max","max"}, - {"Sum","sum"}, - {"BigCount","count"}, - {"StDev","stddev_samp"}, - {"StDevP","stddev_pop"}, - {"Var","var_samp"}, - {"VarP","var_pop"}, - }; - -#if ENTITIES6 - private static readonly Dictionary BinaryOperatorFunctionNames = new Dictionary() - { - {"@@",Operator.QueryMatch}, - {"operator_tsquery_and",Operator.QueryAnd}, - {"operator_tsquery_or",Operator.QueryOr}, - {"operator_tsquery_contains",Operator.QueryContains}, - {"operator_tsquery_is_contained",Operator.QueryIsContained} - }; -#endif - - protected SqlBaseGenerator() - { - } - - private void EnterExpression(PendingProjectsNode n) - { - _currentExpressions.Add(n.Last.Exp); - } - private void LeaveExpression(PendingProjectsNode n) - { - _currentExpressions.Remove(n.Last.Exp); - } - - protected string NextAlias() - { - return "Alias" + _aliasCounter++; - } - - private bool IsCompatible(InputExpression child, DbExpressionKind parentKind) - { - switch (parentKind) - { - case DbExpressionKind.Filter: - return - child.Projection == null && - child.GroupBy == null && - child.Skip == null && - child.Limit == null; - case DbExpressionKind.GroupBy: - return - child.Projection == null && - child.GroupBy == null && - child.Distinct == false && - child.OrderBy == null && - child.Skip == null && - child.Limit == null; - case DbExpressionKind.Distinct: - return - child.OrderBy == null && - child.Skip == null && - child.Limit == null; - case DbExpressionKind.Sort: - return - child.Projection == null && - child.GroupBy == null && - child.Skip == null && - child.Limit == null; - case DbExpressionKind.Skip: - return - child.Projection == null && - child.Skip == null && - child.Limit == null; - case DbExpressionKind.Project: - return - child.Projection == null && - child.Distinct == false; - // Limit and NewInstance are always true - default: - throw new ArgumentException("Unexpected parent expression kind"); - } - } - - private PendingProjectsNode GetInput(DbExpression expression, string childBindingName, string parentBindingName, DbExpressionKind parentKind) - { - PendingProjectsNode n = VisitInputWithBinding(expression, childBindingName); - if (!IsCompatible(n.Last.Exp, parentKind)) - { - n.Selects.Add(new NameAndInputExpression(parentBindingName, new InputExpression(n.Last.Exp, n.Last.AsName))); - } - return n; - } - - private PendingProjectsNode VisitInputWithBinding(DbExpression expression, string bindingName) - { - PendingProjectsNode n; - switch (expression.ExpressionKind) - { - case DbExpressionKind.Scan: - { - ScanExpression scan = (ScanExpression)expression.Accept(this); - InputExpression input = new InputExpression(scan, bindingName); - n = new PendingProjectsNode(bindingName, input); - - break; - } - case DbExpressionKind.Filter: - { - DbFilterExpression exp = (DbFilterExpression)expression; - n = GetInput(exp.Input.Expression, exp.Input.VariableName, bindingName, expression.ExpressionKind); - EnterExpression(n); - VisitedExpression pred = exp.Predicate.Accept(this); - if (n.Last.Exp.Where == null) - n.Last.Exp.Where = new WhereExpression(pred); - else - n.Last.Exp.Where.And(pred); - LeaveExpression(n); - - break; - } - case DbExpressionKind.Sort: - { - DbSortExpression exp = (DbSortExpression)expression; - n = GetInput(exp.Input.Expression, exp.Input.VariableName, bindingName, expression.ExpressionKind); - EnterExpression(n); - n.Last.Exp.OrderBy = new OrderByExpression(); - foreach (var order in exp.SortOrder) - { - n.Last.Exp.OrderBy.AppendSort(order.Expression.Accept(this), order.Ascending); - } - LeaveExpression(n); - - break; - } - case DbExpressionKind.Skip: - { - DbSkipExpression exp = (DbSkipExpression)expression; - n = GetInput(exp.Input.Expression, exp.Input.VariableName, bindingName, expression.ExpressionKind); - EnterExpression(n); - n.Last.Exp.OrderBy = new OrderByExpression(); - foreach (var order in exp.SortOrder) - { - n.Last.Exp.OrderBy.AppendSort(order.Expression.Accept(this), order.Ascending); - } - n.Last.Exp.Skip = new SkipExpression(exp.Count.Accept(this)); - LeaveExpression(n); - break; - } - case DbExpressionKind.Distinct: - { - DbDistinctExpression exp = (DbDistinctExpression)expression; - string childBindingName = NextAlias(); - - n = VisitInputWithBinding(exp.Argument, childBindingName); - if (!IsCompatible(n.Last.Exp, expression.ExpressionKind)) - { - InputExpression prev = n.Last.Exp; - string prevName = n.Last.AsName; - InputExpression input = new InputExpression(prev, prevName); - n.Selects.Add(new NameAndInputExpression(bindingName, input)); - - // We need to copy all the projected columns so the DISTINCT keyword will work on the correct columns - // A parent project expression is never compatible with this new expression, - // so these are the columns that finally will be projected, as wanted - foreach (ColumnExpression col in prev.Projection.Arguments) - { - input.ColumnsToProject.Add(new StringPair(prevName, col.Name), col.Name); - input.ProjectNewNames.Add(col.Name); - } - } - n.Last.Exp.Distinct = true; - break; - } - case DbExpressionKind.Limit: - { - DbLimitExpression exp = (DbLimitExpression)expression; - n = VisitInputWithBinding(exp.Argument, NextAlias()); - if (n.Last.Exp.Limit != null) - { - FunctionExpression least = new FunctionExpression("LEAST"); - least.AddArgument(n.Last.Exp.Limit.Arg); - least.AddArgument(exp.Limit.Accept(this)); - n.Last.Exp.Limit.Arg = least; - } - else - { - n.Last.Exp.Limit = new LimitExpression(exp.Limit.Accept(this)); - } - break; - } - case DbExpressionKind.NewInstance: - { - DbNewInstanceExpression exp = (DbNewInstanceExpression)expression; - if (exp.Arguments.Count == 1 && exp.Arguments[0].ExpressionKind == DbExpressionKind.Element) - { - n = VisitInputWithBinding(((DbElementExpression)exp.Arguments[0]).Argument, NextAlias()); - if (n.Last.Exp.Limit != null) - { - FunctionExpression least = new FunctionExpression("LEAST"); - least.AddArgument(n.Last.Exp.Limit.Arg); - least.AddArgument(new LiteralExpression("1")); - n.Last.Exp.Limit.Arg = least; - } - else - { - n.Last.Exp.Limit = new LimitExpression(new LiteralExpression("1")); - } - } - else if (exp.Arguments.Count >= 1) - { - LiteralExpression result = new LiteralExpression("("); - for (int i = 0; i < exp.Arguments.Count; ++i) - { - DbExpression arg = exp.Arguments[i]; - var visitedColumn = arg.Accept(this); - if (!(visitedColumn is ColumnExpression)) - visitedColumn = new ColumnExpression(visitedColumn, "C", arg.ResultType); - - result.Append(i == 0 ? "SELECT " : " UNION ALL SELECT "); - result.Append(visitedColumn); - } - result.Append(")"); - n = new PendingProjectsNode(bindingName, new InputExpression(result, bindingName)); - } - else - { - TypeUsage type = ((CollectionType)exp.ResultType.EdmType).TypeUsage; - LiteralExpression result = new LiteralExpression("(SELECT "); - result.Append(new CastExpression(new LiteralExpression("NULL"), GetDbType(type.EdmType))); - result.Append(" LIMIT 0)"); - n = new PendingProjectsNode(bindingName, new InputExpression(result, bindingName)); - } - break; - } - case DbExpressionKind.UnionAll: - case DbExpressionKind.Intersect: - case DbExpressionKind.Except: - { - DbBinaryExpression exp = (DbBinaryExpression)expression; - DbExpressionKind expKind = exp.ExpressionKind; - List list = new List(); - Action func = null; - func = e => - { - if (e.ExpressionKind == expKind && e.ExpressionKind != DbExpressionKind.Except) - { - DbBinaryExpression binaryExp = (DbBinaryExpression)e; - func(binaryExp.Left); - func(binaryExp.Right); - } - else - { - list.Add(VisitInputWithBinding(e, bindingName + "_" + list.Count).Last.Exp); - } - }; - func(exp.Left); - func(exp.Right); - InputExpression input = new InputExpression(new CombinedProjectionExpression(expression.ExpressionKind, list), bindingName); - n = new PendingProjectsNode(bindingName, input); - break; - } - case DbExpressionKind.Project: - { - DbProjectExpression exp = (DbProjectExpression)expression; - PendingProjectsNode child = VisitInputWithBinding(exp.Input.Expression, exp.Input.VariableName); - InputExpression input = child.Last.Exp; - bool enterScope = false; - if (!IsCompatible(input, expression.ExpressionKind)) - { - input = new InputExpression(input, child.Last.AsName); - } - else enterScope = true; - - if (enterScope) EnterExpression(child); - - input.Projection = new CommaSeparatedExpression(); - - DbNewInstanceExpression projection = (DbNewInstanceExpression)exp.Projection; - RowType rowType = projection.ResultType.EdmType as RowType; - for (int i = 0; i < rowType.Properties.Count && i < projection.Arguments.Count; ++i) - { - EdmProperty prop = rowType.Properties[i]; - input.Projection.Arguments.Add(new ColumnExpression(projection.Arguments[i].Accept(this), prop.Name, prop.TypeUsage)); - } - - if (enterScope) LeaveExpression(child); - - n = new PendingProjectsNode(bindingName, input); - break; - } - case DbExpressionKind.GroupBy: - { - DbGroupByExpression exp = (DbGroupByExpression)expression; - PendingProjectsNode child = VisitInputWithBinding(exp.Input.Expression, exp.Input.VariableName); - - // I don't know why the input for GroupBy in EF have two names - _refToNode[exp.Input.GroupVariableName] = child; - - InputExpression input = child.Last.Exp; - bool enterScope = false; - if (!IsCompatible(input, expression.ExpressionKind)) - { - input = new InputExpression(input, child.Last.AsName); - } - else enterScope = true; - - if (enterScope) EnterExpression(child); - - input.Projection = new CommaSeparatedExpression(); - - input.GroupBy = new GroupByExpression(); - RowType rowType = ((CollectionType)(exp.ResultType.EdmType)).TypeUsage.EdmType as RowType; - int columnIndex = 0; - foreach (var key in exp.Keys) - { - VisitedExpression keyColumnExpression = key.Accept(this); - var prop = rowType.Properties[columnIndex]; - input.Projection.Arguments.Add(new ColumnExpression(keyColumnExpression, prop.Name, prop.TypeUsage)); - // have no idea why EF is generating a group by with a constant expression, - // but postgresql doesn't need it. - if (!(key is DbConstantExpression)) - { - input.GroupBy.AppendGroupingKey(keyColumnExpression); - } - ++columnIndex; - } - foreach (var ag in exp.Aggregates) - { - DbFunctionAggregate function = (DbFunctionAggregate)ag; - VisitedExpression functionExpression = VisitFunction(function); - var prop = rowType.Properties[columnIndex]; - input.Projection.Arguments.Add(new ColumnExpression(functionExpression, prop.Name, prop.TypeUsage)); - ++columnIndex; - } - - if (enterScope) LeaveExpression(child); - - n = new PendingProjectsNode(bindingName, input); - break; - } - case DbExpressionKind.CrossJoin: - case DbExpressionKind.FullOuterJoin: - case DbExpressionKind.InnerJoin: - case DbExpressionKind.LeftOuterJoin: - case DbExpressionKind.CrossApply: - case DbExpressionKind.OuterApply: - { - InputExpression input = new InputExpression(); - n = new PendingProjectsNode(bindingName, input); - - JoinExpression from = VisitJoinChildren(expression, input, n); - - input.From = from; - - break; - } - default: throw new NotImplementedException(); - } - _refToNode[bindingName] = n; - return n; - } - - private bool IsJoin(DbExpressionKind kind) - { - switch (kind) - { - case DbExpressionKind.CrossJoin: - case DbExpressionKind.FullOuterJoin: - case DbExpressionKind.InnerJoin: - case DbExpressionKind.LeftOuterJoin: - case DbExpressionKind.CrossApply: - case DbExpressionKind.OuterApply: - return true; - } - return false; - } - - private JoinExpression VisitJoinChildren(DbExpression expression, InputExpression input, PendingProjectsNode n) - { - DbExpressionBinding left, right; - DbExpression condition = null; - if (expression.ExpressionKind == DbExpressionKind.CrossJoin) - { - left = ((DbCrossJoinExpression)expression).Inputs[0]; - right = ((DbCrossJoinExpression)expression).Inputs[1]; - if (((DbCrossJoinExpression)expression).Inputs.Count > 2) - { - // I have never seen more than 2 inputs in CrossJoin - throw new NotImplementedException(); - } - } - else if (expression.ExpressionKind == DbExpressionKind.CrossApply || expression.ExpressionKind == DbExpressionKind.OuterApply) - { - left = ((DbApplyExpression)expression).Input; - right = ((DbApplyExpression)expression).Apply; - } - else - { - left = ((DbJoinExpression)expression).Left; - right = ((DbJoinExpression)expression).Right; - condition = ((DbJoinExpression)expression).JoinCondition; - } - - return VisitJoinChildren(left.Expression, left.VariableName, right.Expression, right.VariableName, expression.ExpressionKind, condition, input, n); - } - private JoinExpression VisitJoinChildren(DbExpression left, string leftName, DbExpression right, string rightName, DbExpressionKind joinType, DbExpression condition, InputExpression input, PendingProjectsNode n) - { - JoinExpression join = new JoinExpression(); - join.JoinType = joinType; - - if (IsJoin(left.ExpressionKind)) - { - join.Left = VisitJoinChildren(left, input, n); - } - else - { - PendingProjectsNode l = VisitInputWithBinding(left, leftName); - l.JoinParent = n; - join.Left = new FromExpression(l.Last.Exp, l.Last.AsName); - } - - if (joinType == DbExpressionKind.OuterApply || joinType == DbExpressionKind.CrossApply) - { - EnterExpression(n); - PendingProjectsNode r = VisitInputWithBinding(right, rightName); - LeaveExpression(n); - r.JoinParent = n; - join.Right = new FromExpression(r.Last.Exp, r.Last.AsName) { ForceSubquery = true }; - } - else - { - if (IsJoin(right.ExpressionKind)) - { - join.Right = VisitJoinChildren(right, input, n); - } - else - { - PendingProjectsNode r = VisitInputWithBinding(right, rightName); - r.JoinParent = n; - join.Right = new FromExpression(r.Last.Exp, r.Last.AsName); - } - } - - if (condition != null) - { - EnterExpression(n); - join.Condition = condition.Accept(this); - LeaveExpression(n); - } - return join; - } - - public override VisitedExpression Visit(DbVariableReferenceExpression expression) - { - //return new VariableReferenceExpression(expression.VariableName, _variableSubstitution); - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbUnionAllExpression expression) - { - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbTreatExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbSkipExpression expression) - { - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbSortExpression expression) - { - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbScanExpression expression) - { - MetadataProperty metadata; - string tableName; - string overrideTable = "http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Name"; - if (expression.Target.MetadataProperties.TryGetValue(overrideTable, false, out metadata) && metadata.Value != null) - { - tableName = metadata.Value.ToString(); - } - else if (expression.Target.MetadataProperties.TryGetValue("Table", false, out metadata) && metadata.Value != null) - { - tableName = metadata.Value.ToString(); - } - else - { - tableName = expression.Target.Name; - } - - if (expression.Target.MetadataProperties.Contains("DefiningQuery")) - { - MetadataProperty definingQuery = expression.Target.MetadataProperties.GetValue("DefiningQuery", false); - if (definingQuery.Value != null) - { - return new ScanExpression("(" + definingQuery.Value + ")", expression.Target); - } - } - - ScanExpression scan; - string overrideSchema = "http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Schema"; - if (expression.Target.MetadataProperties.TryGetValue(overrideSchema, false, out metadata) && metadata.Value != null) - { - scan = new ScanExpression(QuoteIdentifier(metadata.Value.ToString()) + "." + QuoteIdentifier(tableName), expression.Target); - } - else if (expression.Target.MetadataProperties.TryGetValue("Schema", false, out metadata) && metadata.Value != null) - { - scan = new ScanExpression(QuoteIdentifier(metadata.Value.ToString()) + "." + QuoteIdentifier(tableName), expression.Target); - } - else - { - scan = new ScanExpression(QuoteIdentifier(expression.Target.EntityContainer.Name) + "." + QuoteIdentifier(tableName), expression.Target); - } - - return scan; - } - - public override VisitedExpression Visit(DbRelationshipNavigationExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbRefExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbQuantifierExpression expression) - { - // TODO: EXISTS or NOT EXISTS depending on expression.ExpressionKind - // comes with it's built in test (subselect for EXISTS) - // This kind of expression is never even created in the EF6 code base - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbProjectExpression expression) - { - return VisitInputWithBinding(expression, NextAlias()).Last.Exp; - } - - public override VisitedExpression Visit(DbParameterReferenceExpression expression) - { - // use parameter in sql - return new LiteralExpression("@" + expression.ParameterName); - } - - public override VisitedExpression Visit(DbOrExpression expression) - { - return OperatorExpression.Build(Operator.Or, _useNewPrecedences, expression.Left.Accept(this), expression.Right.Accept(this)); - } - - public override VisitedExpression Visit(DbOfTypeExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbNullExpression expression) - { - // select does something different here. But insert, update, delete, and functions can just use - // a NULL literal. - return new LiteralExpression("NULL"); - } - - public override VisitedExpression Visit(DbNotExpression expression) - { - // argument can be a "NOT EXISTS" or similar operator that can be negated. - // Convert the not if that's the case - VisitedExpression argument = expression.Argument.Accept(this); - return OperatorExpression.Negate(argument, _useNewPrecedences); - } - - public override VisitedExpression Visit(DbNewInstanceExpression expression) - { - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbLimitExpression expression) - { - // Normally handled by VisitInputWithBinding - - // Otherwise, it is (probably) a child of a DbElementExpression, - // in which case the child of this expression might be a DbProjectExpression, - // then the correct columns will be projected since Limit is compatible with the result of a DbProjectExpression, - // which will result in having a Projection on the node after visiting it. - PendingProjectsNode node = VisitInputWithBinding(expression, NextAlias()); - if (node.Last.Exp.Projection == null) - { - // This DbLimitExpression is (probably) a child of DbElementExpression - // and this expression's child is not a DbProjectExpression, but we should - // find a DbProjectExpression if we look deeper in the command tree. - // The child of this expression is (probably) a DbSortExpression or something else - // that will (probably) be an ancestor to a DbProjectExpression. - - // Since this is (probably) a child of DbElementExpression, we want the first column, - // so make sure it is propagated from the nearest explicit projection. - - CommaSeparatedExpression projection = node.Selects[0].Exp.Projection; - for (int i = 1; i < node.Selects.Count; i++) - { - ColumnExpression column = (ColumnExpression)projection.Arguments[0]; - - node.Selects[i].Exp.ColumnsToProject[new StringPair(node.Selects[i - 1].AsName, column.Name)] = column.Name; - } - } - return node.Last.Exp; - } - - public override VisitedExpression Visit(DbLikeExpression expression) - { - // LIKE keyword - return OperatorExpression.Build(Operator.Like, _useNewPrecedences, expression.Argument.Accept(this), expression.Pattern.Accept(this)); - } - - public override VisitedExpression Visit(DbJoinExpression expression) - { - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbIsOfExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbIsNullExpression expression) - { - return OperatorExpression.Build(Operator.IsNull, _useNewPrecedences, expression.Argument.Accept(this)); - } - - public override VisitedExpression Visit(DbIsEmptyExpression expression) - { - // NOT EXISTS - return OperatorExpression.Negate(new ExistsExpression(expression.Argument.Accept(this)), _useNewPrecedences); - } - - public override VisitedExpression Visit(DbIntersectExpression expression) - { - // INTERSECT keyword - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbGroupByExpression expression) - { - // Normally handled by VisitInputWithBinding - - // Otherwise, it is (probably) a child of a DbElementExpression. - // Group by always projects the correct columns. - return VisitInputWithBinding(expression, NextAlias()).Last.Exp; - } - - public override VisitedExpression Visit(DbRefKeyExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbEntityRefExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbFunctionExpression expression) - { - // a function call - // may be built in, canonical, or user defined - return VisitFunction(expression.Function, expression.Arguments, expression.ResultType); - } - - public override VisitedExpression Visit(DbFilterExpression expression) - { - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbExceptExpression expression) - { - // EXCEPT keyword - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbElementExpression expression) - { - // If child of DbNewInstanceExpression, this is handled in VisitInputWithBinding - - // a scalar expression (ie ExecuteScalar) - // so it will likely be translated into a select - //throw new NotImplementedException(); - LiteralExpression scalar = new LiteralExpression("("); - scalar.Append(expression.Argument.Accept(this)); - scalar.Append(")"); - return scalar; - } - - public override VisitedExpression Visit(DbDistinctExpression expression) - { - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbDerefExpression expression) - { - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbCrossJoinExpression expression) - { - // join without ON - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbConstantExpression expression) - { - if (_createParametersForConstants) - { - NpgsqlParameter parameter = new NpgsqlParameter(); - parameter.ParameterName = "p_" + _parameterCount++; - parameter.NpgsqlDbType = NpgsqlProviderManifest.GetNpgsqlDbType(((PrimitiveType)expression.ResultType.EdmType).PrimitiveTypeKind); - parameter.Value = expression.Value; - _command.Parameters.Add(parameter); - return new LiteralExpression("@" + parameter.ParameterName); - } - else - { - return new ConstantExpression(expression.Value, expression.ResultType); - } - } - - public override VisitedExpression Visit(DbComparisonExpression expression) - { - Operator comparisonOperator; - switch (expression.ExpressionKind) - { - case DbExpressionKind.Equals: comparisonOperator = Operator.Equals; break; - case DbExpressionKind.GreaterThan: comparisonOperator = Operator.GreaterThan; break; - case DbExpressionKind.GreaterThanOrEquals: comparisonOperator = Operator.GreaterThanOrEquals; break; - case DbExpressionKind.LessThan: comparisonOperator = Operator.LessThan; break; - case DbExpressionKind.LessThanOrEquals: comparisonOperator = Operator.LessThanOrEquals; break; - case DbExpressionKind.Like: comparisonOperator = Operator.Like; break; - case DbExpressionKind.NotEquals: comparisonOperator = Operator.NotEquals; break; - default: throw new NotSupportedException(); - } - return OperatorExpression.Build(comparisonOperator, _useNewPrecedences, expression.Left.Accept(this), expression.Right.Accept(this)); - } - - public override VisitedExpression Visit(DbCastExpression expression) - { - return new CastExpression(expression.Argument.Accept(this), GetDbType(expression.ResultType.EdmType)); - } - - protected string GetDbType(EdmType edmType) - { - PrimitiveType primitiveType = edmType as PrimitiveType; - if (primitiveType == null) - throw new NotSupportedException(); - switch (primitiveType.PrimitiveTypeKind) - { - case PrimitiveTypeKind.Boolean: - return "bool"; - case PrimitiveTypeKind.SByte: - case PrimitiveTypeKind.Byte: - case PrimitiveTypeKind.Int16: - return "int2"; - case PrimitiveTypeKind.Int32: - return "int4"; - case PrimitiveTypeKind.Int64: - return "int8"; - case PrimitiveTypeKind.String: - return "text"; - case PrimitiveTypeKind.Decimal: - return "numeric"; - case PrimitiveTypeKind.Single: - return "float4"; - case PrimitiveTypeKind.Double: - return "float8"; - case PrimitiveTypeKind.DateTime: - return "timestamp"; - case PrimitiveTypeKind.DateTimeOffset: - return "timestamptz"; - case PrimitiveTypeKind.Time: - return "interval"; - case PrimitiveTypeKind.Binary: - return "bytea"; - case PrimitiveTypeKind.Guid: - return "uuid"; - } - throw new NotSupportedException(); - } - - public override VisitedExpression Visit(DbCaseExpression expression) - { - LiteralExpression caseExpression = new LiteralExpression(" CASE "); - for (int i = 0; i < expression.When.Count && i < expression.Then.Count; ++i) - { - caseExpression.Append(" WHEN ("); - caseExpression.Append(expression.When[i].Accept(this)); - caseExpression.Append(") THEN ("); - caseExpression.Append(expression.Then[i].Accept(this)); - caseExpression.Append(")"); - } - if (expression.Else is DbNullExpression) - { - caseExpression.Append(" END "); - } - else - { - caseExpression.Append(" ELSE ("); - caseExpression.Append(expression.Else.Accept(this)); - caseExpression.Append(") END "); - } - return caseExpression; - } - - public override VisitedExpression Visit(DbArithmeticExpression expression) - { - Operator arithmeticOperator; - - switch (expression.ExpressionKind) - { - case DbExpressionKind.Divide: - arithmeticOperator = Operator.Div; - break; - case DbExpressionKind.Minus: - arithmeticOperator = Operator.Sub; - break; - case DbExpressionKind.Modulo: - arithmeticOperator = Operator.Mod; - break; - case DbExpressionKind.Multiply: - arithmeticOperator = Operator.Mul; - break; - case DbExpressionKind.Plus: - arithmeticOperator = Operator.Add; - break; - case DbExpressionKind.UnaryMinus: - arithmeticOperator = Operator.UnaryMinus; - break; - default: - throw new NotSupportedException(); - } - - if (expression.ExpressionKind == DbExpressionKind.UnaryMinus) - { - System.Diagnostics.Debug.Assert(expression.Arguments.Count == 1); - return OperatorExpression.Build(arithmeticOperator, _useNewPrecedences, expression.Arguments[0].Accept(this)); - } - else - { - System.Diagnostics.Debug.Assert(expression.Arguments.Count == 2); - return OperatorExpression.Build(arithmeticOperator, _useNewPrecedences, expression.Arguments[0].Accept(this), expression.Arguments[1].Accept(this)); - } - } - - public override VisitedExpression Visit(DbApplyExpression expression) - { - // like a join, but used when the right hand side (the Apply part) is a function. - // it lets you return the results of a function call given values from the - // left hand side (the Input part). - // sql standard is lateral join - - // Handled by VisitInputWithBinding - throw new NotImplementedException(); - } - - public override VisitedExpression Visit(DbAndExpression expression) - { - return OperatorExpression.Build(Operator.And, _useNewPrecedences, expression.Left.Accept(this), expression.Right.Accept(this)); - } - - public override VisitedExpression Visit(DbExpression expression) - { - // only concrete types visited - throw new NotSupportedException(); - } - - public abstract void BuildCommand(DbCommand command); - - internal static string QuoteIdentifier(string identifier) - { - return "\"" + identifier.Replace("\"", "\"\"") + "\""; - } - - private VisitedExpression VisitFunction(DbFunctionAggregate functionAggregate) - { - if (functionAggregate.Function.NamespaceName == "Edm") - { - FunctionExpression aggregate; - try - { - aggregate = new FunctionExpression(AggregateFunctionNames[functionAggregate.Function.Name]); - } catch (KeyNotFoundException) - { - throw new NotSupportedException(); - } - System.Diagnostics.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); - return new CastExpression(aggregate, GetDbType(functionAggregate.ResultType.EdmType)); - } - throw new NotSupportedException(); - } - - private VisitedExpression VisitFunction(EdmFunction function, IList args, TypeUsage resultType) - { - if (function.NamespaceName == "Edm") - { - VisitedExpression arg; - switch (function.Name) - { - // string functions - case "Concat": - System.Diagnostics.Debug.Assert(args.Count == 2); - return OperatorExpression.Build(Operator.Concat, _useNewPrecedences, args[0].Accept(this), args[1].Accept(this)); - case "Contains": - System.Diagnostics.Debug.Assert(args.Count == 2); - FunctionExpression contains = new FunctionExpression("position"); - arg = args[1].Accept(this); - arg.Append(" in "); - arg.Append(args[0].Accept(this)); - contains.AddArgument(arg); - // if position returns zero, then contains is false - return OperatorExpression.Build(Operator.GreaterThan, _useNewPrecedences, contains, new LiteralExpression("0")); - // case "EndsWith": - depends on a reverse function to be able to implement with parameterized queries - case "IndexOf": - System.Diagnostics.Debug.Assert(args.Count == 2); - FunctionExpression indexOf = new FunctionExpression("position"); - arg = args[0].Accept(this); - arg.Append(" in "); - arg.Append(args[1].Accept(this)); - indexOf.AddArgument(arg); - return indexOf; - case "Left": - System.Diagnostics.Debug.Assert(args.Count == 2); - return Substring(args[0].Accept(this), new LiteralExpression(" 1 "), args[1].Accept(this)); - case "Length": - FunctionExpression length = new FunctionExpression("char_length"); - System.Diagnostics.Debug.Assert(args.Count == 1); - length.AddArgument(args[0].Accept(this)); - return new CastExpression(length, GetDbType(resultType.EdmType)); - case "LTrim": - return StringModifier("ltrim", args); - case "Replace": - FunctionExpression replace = new FunctionExpression("replace"); - System.Diagnostics.Debug.Assert(args.Count == 3); - replace.AddArgument(args[0].Accept(this)); - replace.AddArgument(args[1].Accept(this)); - replace.AddArgument(args[2].Accept(this)); - return replace; - // case "Reverse": - case "Right": - System.Diagnostics.Debug.Assert(args.Count == 2); - { - var arg0 = args[0].Accept(this); - var arg1 = args[1].Accept(this); - var start = new FunctionExpression("char_length"); - start.AddArgument(arg0); - // add one before subtracting count since strings are 1 based in postgresql - return Substring(arg0, OperatorExpression.Build(Operator.Sub, _useNewPrecedences, OperatorExpression.Build(Operator.Add, _useNewPrecedences, start, new LiteralExpression("1")), arg1)); - } - case "RTrim": - return StringModifier("rtrim", args); - case "Substring": - System.Diagnostics.Debug.Assert(args.Count == 3); - return Substring(args[0].Accept(this), args[1].Accept(this), args[2].Accept(this)); - case "StartsWith": - System.Diagnostics.Debug.Assert(args.Count == 2); - FunctionExpression startsWith = new FunctionExpression("position"); - arg = args[1].Accept(this); - arg.Append(" in "); - arg.Append(args[0].Accept(this)); - startsWith.AddArgument(arg); - return OperatorExpression.Build(Operator.Equals, _useNewPrecedences, startsWith, new LiteralExpression("1")); - case "ToLower": - return StringModifier("lower", args); - case "ToUpper": - return StringModifier("upper", args); - case "Trim": - return StringModifier("btrim", args); - - // date functions - // date functions - case "AddDays": - case "AddHours": - case "AddMicroseconds": - case "AddMilliseconds": - case "AddMinutes": - case "AddMonths": - case "AddNanoseconds": - case "AddSeconds": - case "AddYears": - return DateAdd(function.Name, args); - case "DiffDays": - case "DiffHours": - case "DiffMicroseconds": - case "DiffMilliseconds": - case "DiffMinutes": - case "DiffMonths": - case "DiffNanoseconds": - case "DiffSeconds": - case "DiffYears": - System.Diagnostics.Debug.Assert(args.Count == 2); - return DateDiff(function.Name, args[0].Accept(this), args[1].Accept(this)); - case "Day": - case "Hour": - case "Minute": - case "Month": - case "Second": - case "Year": - return DatePart(function.Name, args); - case "Millisecond": - return DatePart("milliseconds", args); - case "GetTotalOffsetMinutes": - VisitedExpression timezone = DatePart("timezone", args); - return OperatorExpression.Build(Operator.Div, _useNewPrecedences, timezone, new LiteralExpression("60")); - case "CurrentDateTime": - return new LiteralExpression("LOCALTIMESTAMP"); - case "CurrentUtcDateTime": - LiteralExpression utcNow = new LiteralExpression("CURRENT_TIMESTAMP"); - utcNow.Append(" AT TIME ZONE 'UTC'"); - return utcNow; - case "CurrentDateTimeOffset": - // TODO: this doesn't work yet because the reader - // doesn't return DateTimeOffset. - return new LiteralExpression("CURRENT_TIMESTAMP"); - - // bitwise operators - case "BitwiseAnd": - return BitwiseOperator(args, Operator.BitwiseAnd); - case "BitwiseOr": - return BitwiseOperator(args, Operator.BitwiseOr); - case "BitwiseXor": - return BitwiseOperator(args, Operator.BitwiseXor); - case "BitwiseNot": - System.Diagnostics.Debug.Assert(args.Count == 1); - return OperatorExpression.Build(Operator.BitwiseNot, _useNewPrecedences, args[0].Accept(this)); - - // math operators - case "Abs": - case "Ceiling": - case "Floor": - return UnaryMath(function.Name, args); - case "Round": - return (args.Count == 1) ? UnaryMath(function.Name, args) : BinaryMath(function.Name, args); - case "Power": - return BinaryMath(function.Name, args); - case "Truncate": - return BinaryMath("trunc", args); - - case "NewGuid": - return new FunctionExpression("uuid_generate_v4"); - case "TruncateTime": - return new TruncateTimeExpression("day", args[0].Accept(this)); - - default: - throw new NotSupportedException("NotSupported " + function.Name); - } - } - -#if ENTITIES6 - var functionName = function.StoreFunctionNameAttribute ?? function.Name; - if (function.NamespaceName == "Npgsql") - { - Operator binaryOperator; - if (BinaryOperatorFunctionNames.TryGetValue(functionName, out binaryOperator)) - { - if (args.Count != 2) - throw new ArgumentException($"Invalid number of {functionName} arguments. Expected 2.", nameof(args)); - - return OperatorExpression.Build( - binaryOperator, - _useNewPrecedences, - args[0].Accept(this), - args[1].Accept(this)); - } - - if (functionName == "operator_tsquery_negate") - { - if (args.Count != 1) - throw new ArgumentException("Invalid number of operator_tsquery_not arguments. Expected 1.", nameof(args)); - - return OperatorExpression.Build(Operator.QueryNegate, _useNewPrecedences, args[0].Accept(this)); - } - - if (functionName == "ts_rank" || functionName == "ts_rank_cd") - { - if (args.Count > 4) - { - var weightD = args[0] as DbConstantExpression; - var weightC = args[1] as DbConstantExpression; - var weightB = args[2] as DbConstantExpression; - var weightA = args[3] as DbConstantExpression; - - if (weightD == null || weightC == null || weightB == null || weightA == null) - throw new NotSupportedException("All weight values must be constant expressions."); - - var newValue = string.Format( - CultureInfo.InvariantCulture, - "{{ {0:r}, {1:r}, {2:r}, {3:r} }}", - weightD.Value, - weightC.Value, - weightB.Value, - weightA.Value); - - args = new[] { DbExpression.FromString(newValue) }.Concat(args.Skip(4)).ToList(); - } - } - else if (functionName == "setweight") - { - if (args.Count != 2) - throw new ArgumentException("Invalid number of setweight arguments. Expected 2.", nameof(args)); - - var weightLabelExpression = args[1] as DbConstantExpression; - if (weightLabelExpression == null) - throw new NotSupportedException("setweight label argument must be a constant expression."); - - var weightLabel = (NpgsqlWeightLabel)weightLabelExpression.Value; - if (!Enum.IsDefined(typeof(NpgsqlWeightLabel), weightLabelExpression.Value)) - throw new NotSupportedException("Unsupported weight label value: " + weightLabel); - - args = new[] { args[0], DbExpression.FromString(weightLabel.ToString()) }; - } - else if (functionName == "as_tsvector") - { - if (args.Count != 1) - throw new ArgumentException("Invalid number of arguments. Expected 1.", nameof(args)); - - return new CastExpression(args[0].Accept(this), "tsvector"); - } - else if (functionName == "as_tsquery") - { - if (args.Count != 1) - throw new ArgumentException("Invalid number of arguments. Expected 1.", nameof(args)); - - return new CastExpression(args[0].Accept(this), "tsquery"); - } - } - - var customFuncCall = new FunctionExpression( - string.IsNullOrEmpty(function.Schema) - ? QuoteIdentifier(functionName) - : QuoteIdentifier(function.Schema) + "." + QuoteIdentifier(functionName) - ); - - foreach (var a in args) - customFuncCall.AddArgument(a.Accept(this)); - return customFuncCall; -#else - throw new NotSupportedException(); -#endif - } - - private VisitedExpression Substring(VisitedExpression source, VisitedExpression start, VisitedExpression count) - { - FunctionExpression substring = new FunctionExpression("substr"); - substring.AddArgument(source); - substring.AddArgument(start); - substring.AddArgument(count); - return substring; - } - - private VisitedExpression Substring(VisitedExpression source, VisitedExpression start) - { - FunctionExpression substring = new FunctionExpression("substr"); - substring.AddArgument(source); - substring.AddArgument(start); - return substring; - } - - private VisitedExpression UnaryMath(string funcName, IList args) - { - FunctionExpression mathFunction = new FunctionExpression(funcName); - System.Diagnostics.Debug.Assert(args.Count == 1); - mathFunction.AddArgument(args[0].Accept(this)); - return mathFunction; - } - - private VisitedExpression BinaryMath(string funcName, IList args) - { - FunctionExpression mathFunction = new FunctionExpression(funcName); - System.Diagnostics.Debug.Assert(args.Count == 2); - mathFunction.AddArgument(args[0].Accept(this)); - mathFunction.AddArgument(args[1].Accept(this)); - return mathFunction; - } - - private VisitedExpression StringModifier(string modifier, IList args) - { - FunctionExpression modifierFunction = new FunctionExpression(modifier); - System.Diagnostics.Debug.Assert(args.Count == 1); - modifierFunction.AddArgument(args[0].Accept(this)); - return modifierFunction; - } - - private VisitedExpression DatePart(string partName, IList args) - { - - FunctionExpression extract_date = new FunctionExpression("cast(extract"); - System.Diagnostics.Debug.Assert(args.Count == 1); - VisitedExpression arg = new LiteralExpression(partName + " FROM "); - arg.Append(args[0].Accept(this)); - extract_date.AddArgument(arg); - // need to convert to Int32 to match cononical function - extract_date.Append(" as int4)"); - return extract_date; - } - - /// - /// PostgreSQL has no direct functions to implements DateTime canonical functions - /// http://msdn.microsoft.com/en-us/library/bb738626.aspx - /// http://msdn.microsoft.com/en-us/library/bb738626.aspx - /// but we can use workaround: - /// expression + number * INTERVAL '1 number_type' - /// where number_type is the number type (days, years and etc) - /// - /// - /// - /// - private VisitedExpression DateAdd(string functionName, IList args) - { - bool nano = false; - string part = functionName.Substring(3); - - if (part == "Nanoseconds") - { - nano = true; - part = "Microseconds"; - } - - System.Diagnostics.Debug.Assert(args.Count == 2); - VisitedExpression time = args[0].Accept(this); - VisitedExpression mulLeft = args[1].Accept(this); - if (nano) - mulLeft = OperatorExpression.Build(Operator.Div, _useNewPrecedences, mulLeft, new LiteralExpression("1000")); - LiteralExpression mulRight = new LiteralExpression(String.Format("INTERVAL '1 {0}'", part)); - return OperatorExpression.Build(Operator.Add, _useNewPrecedences, time, OperatorExpression.Build(Operator.Mul, _useNewPrecedences, mulLeft, mulRight)); - } - - private VisitedExpression DateDiff(string functionName, VisitedExpression start, VisitedExpression end) - { - switch (functionName) - { - case "DiffDays": - start = new FunctionExpression("date_trunc").AddArgument("'day'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'day'").AddArgument(end); - return new FunctionExpression("date_part").AddArgument("'day'").AddArgument( - OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start) - ).Append("::int4"); - case "DiffHours": - { - start = new FunctionExpression("date_trunc").AddArgument("'hour'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'hour'").AddArgument(end); - LiteralExpression epoch = new LiteralExpression("epoch from "); - OperatorExpression diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); - epoch.Append(diff); - return OperatorExpression.Build(Operator.Div, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch).Append("::int4"), new LiteralExpression("3600")); - } - case "DiffMicroseconds": - { - start = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(end); - LiteralExpression epoch = new LiteralExpression("epoch from "); - OperatorExpression diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); - epoch.Append(diff); - return new CastExpression(OperatorExpression.Build(Operator.Mul, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch), new LiteralExpression("1000000")), "int4"); - } - case "DiffMilliseconds": - { - start = new FunctionExpression("date_trunc").AddArgument("'milliseconds'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'milliseconds'").AddArgument(end); - LiteralExpression epoch = new LiteralExpression("epoch from "); - OperatorExpression diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); - epoch.Append(diff); - return new CastExpression(OperatorExpression.Build(Operator.Mul, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch), new LiteralExpression("1000")), "int4"); - } - case "DiffMinutes": - { - start = new FunctionExpression("date_trunc").AddArgument("'minute'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'minute'").AddArgument(end); - LiteralExpression epoch = new LiteralExpression("epoch from "); - OperatorExpression diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); - epoch.Append(diff); - return OperatorExpression.Build(Operator.Div, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch).Append("::int4"), new LiteralExpression("60")); - } - case "DiffMonths": - { - start = new FunctionExpression("date_trunc").AddArgument("'month'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'month'").AddArgument(end); - VisitedExpression age = new FunctionExpression("age").AddArgument(end).AddArgument(start); - - // A month is 30 days and a year is 365.25 days after conversion from interval to seconds. - // After rounding and casting, the result will contain the correct number of months as an int4. - FunctionExpression seconds = new FunctionExpression("extract").AddArgument(new LiteralExpression("epoch from ").Append(age)); - VisitedExpression months = OperatorExpression.Build(Operator.Div, _useNewPrecedences, seconds, new LiteralExpression("2629800.0")); - return new FunctionExpression("round").AddArgument(months).Append("::int4"); - } - case "DiffNanoseconds": - { - // PostgreSQL only supports microseconds precision, so the value will be a multiple of 1000 - // This date_trunc will make sure start and end are of type timestamp, e.g. if the arguments is of type date - start = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'microseconds'").AddArgument(end); - LiteralExpression epoch = new LiteralExpression("epoch from "); - OperatorExpression diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); - epoch.Append(diff); - return new CastExpression(OperatorExpression.Build(Operator.Mul, _useNewPrecedences, new FunctionExpression("extract").AddArgument(epoch), new LiteralExpression("1000000000")), "int4"); - } - case "DiffSeconds": - { - start = new FunctionExpression("date_trunc").AddArgument("'second'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'second'").AddArgument(end); - LiteralExpression epoch = new LiteralExpression("epoch from "); - OperatorExpression diff = OperatorExpression.Build(Operator.Sub, _useNewPrecedences, end, start); - epoch.Append(diff); - return new FunctionExpression("extract").AddArgument(epoch).Append("::int4"); - } - case "DiffYears": - { - start = new FunctionExpression("date_trunc").AddArgument("'year'").AddArgument(start); - end = new FunctionExpression("date_trunc").AddArgument("'year'").AddArgument(end); - VisitedExpression age = new FunctionExpression("age").AddArgument(end).AddArgument(start); - return new FunctionExpression("date_part").AddArgument("'year'").AddArgument(age).Append("::int4"); - } - default: throw new NotSupportedException("Internal error: unknown function name " + functionName); - } - } - - private VisitedExpression BitwiseOperator(IList args, Operator oper) - { - System.Diagnostics.Debug.Assert(args.Count == 2); - return OperatorExpression.Build(oper, _useNewPrecedences, args[0].Accept(this), args[1].Accept(this)); - } - -#if ENTITIES6 - public override VisitedExpression Visit(DbInExpression expression) - { - VisitedExpression item = expression.Item.Accept(this); - - ConstantExpression[] elements = new ConstantExpression[expression.List.Count]; - for (int i = 0; i < expression.List.Count; i++) - { - elements[i] = (ConstantExpression)expression.List[i].Accept(this); - } - - return OperatorExpression.Build(Operator.In, _useNewPrecedences, item, new ConstantListExpression(elements)); - } - - public override VisitedExpression Visit(DbPropertyExpression expression) - { - // This is overridden in the other visitors - throw new NotImplementedException("New in Entity Framework 6"); - } -#endif - } -} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/SqlDeleteGenerator.cs b/src/EntityFramework6.Npgsql/SqlGenerators/SqlDeleteGenerator.cs deleted file mode 100644 index a0cc522..0000000 --- a/src/EntityFramework6.Npgsql/SqlGenerators/SqlDeleteGenerator.cs +++ /dev/null @@ -1,67 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Data.Common; -#if ENTITIES6 -using System.Data.Entity.Core.Common.CommandTrees; -#else -using System.Data.Common.CommandTrees; -#endif - -namespace Npgsql.SqlGenerators -{ - internal class SqlDeleteGenerator : SqlBaseGenerator - { - private DbDeleteCommandTree _commandTree; - private string _tableName; - - public SqlDeleteGenerator(DbDeleteCommandTree commandTree) - { - _commandTree = commandTree; - } - - public override VisitedExpression Visit(DbPropertyExpression expression) - { - DbVariableReferenceExpression variable = expression.Instance as DbVariableReferenceExpression; - if (variable == null || variable.VariableName != _tableName) - throw new NotSupportedException(); - return new PropertyExpression(expression.Property); - } - - public override void BuildCommand(DbCommand command) - { - // TODO: handle _commandTree.Returning and _commandTree.Parameters - DeleteExpression delete = new DeleteExpression(); - _tableName = _commandTree.Target.VariableName; - delete.AppendFrom(_commandTree.Target.Expression.Accept(this)); - if (_commandTree.Predicate != null) - { - delete.AppendWhere(_commandTree.Predicate.Accept(this)); - } - _tableName = null; - command.CommandText = delete.ToString(); - } - } -} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/SqlInsertGenerator.cs b/src/EntityFramework6.Npgsql/SqlGenerators/SqlInsertGenerator.cs deleted file mode 100644 index b018bca..0000000 --- a/src/EntityFramework6.Npgsql/SqlGenerators/SqlInsertGenerator.cs +++ /dev/null @@ -1,76 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Data.Common; -#if ENTITIES6 -using System.Data.Entity.Core.Common.CommandTrees; -#else -using System.Data.Common.CommandTrees; -#endif - -namespace Npgsql.SqlGenerators -{ - internal class SqlInsertGenerator : SqlBaseGenerator - { - private DbInsertCommandTree _commandTree; - private string _tableName; - - public SqlInsertGenerator(DbInsertCommandTree commandTree) - { - _commandTree = commandTree; - } - - public override VisitedExpression Visit(DbPropertyExpression expression) - { - DbVariableReferenceExpression variable = expression.Instance as DbVariableReferenceExpression; - if (variable == null || variable.VariableName != _tableName) - throw new NotSupportedException(); - return new PropertyExpression(expression.Property); - } - - public override void BuildCommand(DbCommand command) - { - // TODO: handle_commandTree.Parameters - InsertExpression insert = new InsertExpression(); - _tableName = _commandTree.Target.VariableName; - insert.AppendTarget(_commandTree.Target.Expression.Accept(this)); - List columns = new List(); - List values = new List(); - foreach (DbSetClause clause in _commandTree.SetClauses) - { - columns.Add(clause.Property.Accept(this)); - values.Add(clause.Value.Accept(this)); - } - insert.AppendColumns(columns); - insert.AppendValues(values); - if (_commandTree.Returning != null) - { - insert.AppendReturning(_commandTree.Returning as DbNewInstanceExpression); - } - _tableName = null; - command.CommandText = insert.ToString(); - } - } -} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/SqlUpdateGenerator.cs b/src/EntityFramework6.Npgsql/SqlGenerators/SqlUpdateGenerator.cs deleted file mode 100644 index 9912e4e..0000000 --- a/src/EntityFramework6.Npgsql/SqlGenerators/SqlUpdateGenerator.cs +++ /dev/null @@ -1,75 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Data.Common; -#if ENTITIES6 -using System.Data.Entity.Core.Common.CommandTrees; -#else -using System.Data.Common.CommandTrees; -#endif - -namespace Npgsql.SqlGenerators -{ - class SqlUpdateGenerator : SqlBaseGenerator - { - private DbUpdateCommandTree _commandTree; - private string _tableName; - - public SqlUpdateGenerator(DbUpdateCommandTree commandTree) - { - _commandTree = commandTree; - } - - public override VisitedExpression Visit(DbPropertyExpression expression) - { - DbVariableReferenceExpression variable = expression.Instance as DbVariableReferenceExpression; - if (variable == null || variable.VariableName != _tableName) - throw new NotSupportedException(); - return new PropertyExpression(expression.Property); - } - - public override void BuildCommand(DbCommand command) - { - // TODO: handle _commandTree.Parameters - UpdateExpression update = new UpdateExpression(); - _tableName = _commandTree.Target.VariableName; - update.AppendTarget(_commandTree.Target.Expression.Accept(this)); - foreach (DbSetClause clause in _commandTree.SetClauses) - { - update.AppendSet(clause.Property.Accept(this), clause.Value.Accept(this)); - } - if (_commandTree.Predicate != null) - { - update.AppendWhere(_commandTree.Predicate.Accept(this)); - } - if (_commandTree.Returning != null) - { - update.AppendReturning((DbNewInstanceExpression)_commandTree.Returning); - } - _tableName = null; - command.CommandText = update.ToString(); - } - } -} diff --git a/src/EntityFramework6.Npgsql/SqlGenerators/StringPair.cs b/src/EntityFramework6.Npgsql/SqlGenerators/StringPair.cs deleted file mode 100644 index 7cd6779..0000000 --- a/src/EntityFramework6.Npgsql/SqlGenerators/StringPair.cs +++ /dev/null @@ -1,65 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Npgsql.SqlGenerators -{ - /// - /// Used for lookup in a Dictionary, since Tuple is not available in .NET 3.5 - /// - internal class StringPair - { - private string _item1; - private string _item2; - - public string Item1 { get { return _item1; } } - public string Item2 { get { return _item2; } } - - public StringPair(string s1, string s2) - { - _item1 = s1; - _item2 = s2; - } - - public override bool Equals(object obj) - { - if (obj == null) - return false; - - StringPair o = obj as StringPair; - if (o == null) - return false; - - return _item1 == o._item1 && _item2 == o._item2; - } - - public override int GetHashCode() - { - return (_item1 + "." + _item2).GetHashCode(); - } - } -} diff --git a/src/EntityFramework6.Npgsql/install.ps1 b/src/EntityFramework6.Npgsql/install.ps1 deleted file mode 100644 index b6b6c15..0000000 --- a/src/EntityFramework6.Npgsql/install.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -Add-EFProvider $project 'Npgsql' 'Npgsql.NpgsqlServices, EntityFramework6.Npgsql' diff --git a/src/EntityFramework6.Npgsql/packages.config b/src/EntityFramework6.Npgsql/packages.config deleted file mode 100644 index ef18161..0000000 --- a/src/EntityFramework6.Npgsql/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/teamcity_set_version.cmd b/teamcity_set_version.cmd deleted file mode 100644 index f0f2e36..0000000 --- a/teamcity_set_version.cmd +++ /dev/null @@ -1 +0,0 @@ -echo ##teamcity[buildNumber '3.1.0-%1'] diff --git a/test/EntityFramework6.Npgsql.Tests/EntityFramework6.Npgsql.Tests.csproj b/test/EntityFramework6.Npgsql.Tests/EntityFramework6.Npgsql.Tests.csproj deleted file mode 100644 index 1d15360..0000000 --- a/test/EntityFramework6.Npgsql.Tests/EntityFramework6.Npgsql.Tests.csproj +++ /dev/null @@ -1,104 +0,0 @@ - - - - {4A0A42DE-C8B8-11E4-8EC9-005056A163A4} - EntityFramework6.Npgsql.Tests - Debug - AnyCPU - true - ..\..\Npgsql.snk - Library - EntityFramework6.Npgsql.Tests - ..\..\ - true - - v4.6 - - - bin\Debug\ - true - full - false - - - true - 4 - TRACE;DEBUG;NET46 - - - bin\Release\ - - - true - true - 4 - TRACE;NET46 - - - - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll - True - - - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - True - - - ..\..\packages\NLog.4.3.3\lib\net45\NLog.dll - True - - - ..\..\packages\Npgsql.3.1.2\lib\net451\Npgsql.dll - True - - - ..\..\packages\NUnit.3.2.1\lib\net45\nunit.framework.dll - True - - - - 3.5 - - - - - 3.0 - - - - - - - - - - - - - - - - - - - - - - - Always - - - Always - - - Always - - - - - {3ec85cba-5b79-11e3-8104-0022198ab089} - EntityFramework6.Npgsql - - - - \ No newline at end of file diff --git a/test/EntityFramework6.Npgsql.Tests/Program.cs b/test/EntityFramework6.Npgsql.Tests/Program.cs deleted file mode 100644 index 87168b4..0000000 --- a/test/EntityFramework6.Npgsql.Tests/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NUnitLite; - -// Exists as a temporary test runner for dotnet cli -// (see https://github.com/nunit/nunit/issues/1371) -namespace Npgsql.Tests -{ - public class Program - { - public static int Main(string[] args) - { -#if DNXCORE50 - return new AutoRun().Execute(typeof(Program).GetTypeInfo().Assembly, Console.Out, Console.In, args); -#else - return new AutoRun().Execute(args); -#endif - } - } -} diff --git a/test/EntityFramework6.Npgsql.Tests/Properties/AssemblyInfo.cs b/test/EntityFramework6.Npgsql.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index fac0285..0000000 --- a/test/EntityFramework6.Npgsql.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Author: -// Francisco Figueiredo Jr. -// -// Copyright (C) 2002 The Npgsql Development Team -// npgsql-general@gborg.postgresql.org -// http://gborg.postgresql.org/project/npgsql/projdisplay.php -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following -// attributes. -// -// change them to the information which is associated with the assembly -// you compile. - -[assembly: AssemblyTitle("")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all values by your own or you can build default build and revision -// numbers with the '*' character (the default): - -[assembly: AssemblyVersion("1.0.0.0")] - -// The following attributes specify the key for the sign of your assembly. See the -// .NET Framework documentation for more information about signing. -// This is not required, if you don't want signing let these attributes like they're. -[assembly: AssemblyDelaySign(false)] -[assembly: AssemblyKeyFile("")] diff --git a/test/EntityFramework6.Npgsql.Tests/Support/EntityFrameworkTestBase.cs b/test/EntityFramework6.Npgsql.Tests/Support/EntityFrameworkTestBase.cs deleted file mode 100644 index 366a02f..0000000 --- a/test/EntityFramework6.Npgsql.Tests/Support/EntityFrameworkTestBase.cs +++ /dev/null @@ -1,200 +0,0 @@ -#region License -// The PostgreSQL License -// -// Copyright (C) 2016 The Npgsql Development Team -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#endregion - -using Npgsql; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Data.Entity; -using System.Linq; -using System.Text; -using System.ComponentModel.DataAnnotations.Schema; -using System.Data.Entity.Core.Metadata.Edm; -using System.Data.Entity.Core.Objects; -using System.Data.Entity.Infrastructure; -using NpgsqlTypes; - -// ReSharper disable once CheckNamespace -namespace EntityFramework6.Npgsql.Tests -{ - public abstract class EntityFrameworkTestBase : TestBase - { - [OneTimeSetUp] - public new void TestFixtureSetup() - { - using (var context = new BloggingContext(ConnectionString)) - { - if (context.Database.Exists()) - context.Database.Delete();//We delete to be 100% schema is synced - context.Database.Create(); - } - - // Create sequence for the IntComputedValue property. - using (var createSequenceConn = OpenConnection(ConnectionString)) - { - createSequenceConn.ExecuteNonQuery("create sequence blog_int_computed_value_seq"); - createSequenceConn.ExecuteNonQuery("alter table \"dbo\".\"Blogs\" alter column \"IntComputedValue\" set default nextval('blog_int_computed_value_seq');"); - createSequenceConn.ExecuteNonQuery("alter table \"dbo\".\"Posts\" alter column \"VarbitColumn\" type varbit using null"); - createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"StoredAddFunction\"(integer, integer) RETURNS integer AS $$ SELECT $1 + $2; $$ LANGUAGE SQL;"); - createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"StoredEchoFunction\"(integer) RETURNS integer AS $$ SELECT $1; $$ LANGUAGE SQL;"); - } - } - - /// - /// Clean any previous entites before our test - /// - [SetUp] - protected void SetUp() - { - using (var context = new BloggingContext(ConnectionString)) - { - context.Blogs.RemoveRange(context.Blogs); - context.Posts.RemoveRange(context.Posts); - context.SaveChanges(); - } - } - } - - public class Blog - { - public int BlogId { get; set; } - public string Name { get; set; } - - public virtual List Posts { get; set; } - - [DatabaseGenerated(DatabaseGeneratedOption.Computed)] - public int IntComputedValue { get; set; } - } - - public class Post - { - public int PostId { get; set; } - public string Title { get; set; } - public string Content { get; set; } - public byte Rating { get; set; } - public DateTime CreationDate { get; set; } - public string VarbitColumn { get; set; } - public int BlogId { get; set; } - public virtual Blog Blog { get; set; } - } - - public class NoColumnsEntity - { - public int Id { get; set; } - } - - public class BloggingContext : DbContext - { - public BloggingContext(string connection) - : base(new NpgsqlConnection(connection), CreateModel(new NpgsqlConnection(connection)), true) - { - } - - public DbSet Blogs { get; set; } - public DbSet Posts { get; set; } - public DbSet NoColumnsEntities { get; set; } - - [DbFunction("BloggingContext", "ClrStoredAddFunction")] - public static int StoredAddFunction(int val1, int val2) - { - throw new NotSupportedException(); - } - - [DbFunction("BloggingContext", "StoredEchoFunction")] - public static int StoredEchoFunction(int value) - { - throw new NotSupportedException(); - } - - private static DbCompiledModel CreateModel(NpgsqlConnection connection) - { - var dbModelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest); - - // Import Sets - dbModelBuilder.Entity(); - dbModelBuilder.Entity(); - dbModelBuilder.Entity(); - - // Import function - var dbModel = dbModelBuilder.Build(connection); - var edmType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32); - - var addFunc = EdmFunction.Create( - "ClrStoredAddFunction", - "BloggingContext", - DataSpace.SSpace, - new EdmFunctionPayload - { - ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, - Schema = "dbo", - IsComposable = true, - IsNiladic = false, - IsBuiltIn = false, - IsAggregate = false, - IsFromProviderManifest = true, - StoreFunctionName = "StoredAddFunction", - ReturnParameters = new[] - { - FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue) - }, - Parameters = new[] - { - FunctionParameter.Create("Value1", edmType, ParameterMode.In), - FunctionParameter.Create("Value2", edmType, ParameterMode.In) - } - }, - null); - dbModel.StoreModel.AddItem(addFunc); - - var echoFunc = EdmFunction.Create( - "StoredEchoFunction", - "BloggingContext", - DataSpace.SSpace, - new EdmFunctionPayload - { - ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion, - Schema = "dbo", - IsComposable = true, - IsNiladic = false, - IsBuiltIn = false, - IsAggregate = false, - IsFromProviderManifest = true, - StoreFunctionName = null, // intentional - ReturnParameters = new[] - { - FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue) - }, - Parameters = new[] - { - FunctionParameter.Create("Value1", edmType, ParameterMode.In) - } - }, - null); - dbModel.StoreModel.AddItem(echoFunc); - - var compiledModel = dbModel.Compile(); - return compiledModel; - } - } -} diff --git a/test/EntityFramework6.Npgsql.Tests/packages.config b/test/EntityFramework6.Npgsql.Tests/packages.config deleted file mode 100644 index 7c2a037..0000000 --- a/test/EntityFramework6.Npgsql.Tests/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file