diff --git a/.appveyor.yml b/.appveyor.yml index 69bb0c461..f0fbd56fb 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,5 @@ image: Visual Studio 2017 Preview -version: 2.0.0-{build} +version: 2.0.1-{build} services: - postgresql environment: @@ -23,12 +23,11 @@ build_script: - msbuild /p:Configuration=Release - dotnet pack src\EFCore.PG\EFCore.PG.csproj -c Release --version-suffix ci-%PADDED_BUILD_NUMBER% test_script: - - cd test\EFCore.PG.Tests && dotnet xunit && cd ..\..\ - - cd test\EFCore.PG.FunctionalTests && dotnet xunit && cd ..\..\ - - cd test\EFCore.PG.Design.FunctionalTests && dotnet xunit && cd ..\..\ + - dotnet test test\EFCore.PG.Tests\EFCore.PG.Tests.csproj + - dotnet test test\EFCore.PG.FunctionalTests\EFCore.PG.FunctionalTests.csproj + - dotnet test test\EFCore.PG.Design.FunctionalTests\EFCore.PG.Design.FunctionalTests.csproj artifacts: - path: 'src\EFCore.PG\bin\**\*.nupkg' - - path: 'src\EFCore.PG.Design\bin\**\*.nupkg' deploy: - provider: NuGet server: https://www.myget.org/F/npgsql-unstable/api/v2/package diff --git a/.gitignore b/.gitignore index 9a262f91c..4460d7808 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.userprefs /*.nupkg .nuget/ +.vs/ [Bb]in/ [Bb]uild/ [Oo]bj/ diff --git a/.travis.yml b/.travis.yml index d011c923b..abbbda8ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,9 @@ env: before_script: - dotnet restore -v Minimal script: - - cd test/EFCore.PG.Tests && dotnet xunit -framework netcoreapp2.0 && cd ../../ - - cd test/EFCore.PG.FunctionalTests && dotnet xunit -framework netcoreapp2.0 && cd ../../ - - cd test/EFCore.PG.Design.FunctionalTests && dotnet xunit -framework netcoreapp2.0 && cd ../../ + - dotnet test test/EFCore.PG.Tests/EFCore.PG.Tests.csproj + - dotnet test test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj + - dotnet test test/EFCore.PG.Design.FunctionalTests/EFCore.PG.Design.FunctionalTests.csproj cache: directories: diff --git a/src/EFCore.PG/EFCore.PG.csproj b/src/EFCore.PG/EFCore.PG.csproj index 937befc7a..4976d1b97 100644 --- a/src/EFCore.PG/EFCore.PG.csproj +++ b/src/EFCore.PG/EFCore.PG.csproj @@ -1,7 +1,7 @@  - 2.0.0 + 2.0.1 netstandard2.0 Npgsql.EntityFrameworkCore.PostgreSQL Microsoft.EntityFrameworkCore @@ -25,8 +25,8 @@ - - + + diff --git a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs index 9fdbb609d..7c21d3790 100644 --- a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs +++ b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs @@ -787,20 +787,6 @@ public virtual void Transfer( .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(newSchema)); } - protected override void ForeignKeyAction(ReferentialAction referentialAction, MigrationCommandListBuilder builder) - { - Check.NotNull(builder, nameof(builder)); - - if (referentialAction == ReferentialAction.Restrict) - { - builder.Append("NO ACTION"); - } - else - { - base.ForeignKeyAction(referentialAction, builder); - } - } - #endregion Utilities #region System column utilities diff --git a/src/EFCore.PG/NpgsqlDbFunctionsExtensions.cs b/src/EFCore.PG/NpgsqlDbFunctionsExtensions.cs index 6742bd994..64b5ac6a0 100644 --- a/src/EFCore.PG/NpgsqlDbFunctionsExtensions.cs +++ b/src/EFCore.PG/NpgsqlDbFunctionsExtensions.cs @@ -20,7 +20,7 @@ public static bool ILike( [CanBeNull] this DbFunctions _, [CanBeNull] string matchExpression, [CanBeNull] string pattern) - => LikeCore(matchExpression, pattern, escapeCharacter: null); + => ILikeCore(matchExpression, pattern, escapeCharacter: null); /// /// An implementation of the PostgreSQL ILIKE operation, which is an insensitive LIKE. @@ -38,7 +38,7 @@ public static bool ILike( [CanBeNull] string matchExpression, [CanBeNull] string pattern, [CanBeNull] string escapeCharacter) - => LikeCore(matchExpression, pattern, escapeCharacter); + => ILikeCore(matchExpression, pattern, escapeCharacter); // Regex special chars defined here: // https://msdn.microsoft.com/en-us/library/4edbef7e(v=vs.110).aspx @@ -56,7 +56,7 @@ private static string BuildEscapeRegexCharsPattern(IEnumerable regexSpecia return string.Join("|", regexSpecialChars.Select(c => @"\" + c)); } - private static bool LikeCore(string matchExpression, string pattern, string escapeCharacter) + private static bool ILikeCore(string matchExpression, string pattern, string escapeCharacter) { //TODO: this fixes https://github.com/aspnet/EntityFramework/issues/8656 by insisting that // the "escape character" is a string but just using the first character of that string, @@ -133,7 +133,7 @@ var regexPattern return Regex.IsMatch( matchExpression, @"\A" + regexPattern + @"\s*\z", - RegexOptions.Singleline, + RegexOptions.IgnoreCase | RegexOptions.Singleline, _regexTimeout); } } diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlObjectToStringTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlObjectToStringTranslator.cs index 8cd802bc2..9e1c9fc8d 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlObjectToStringTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlObjectToStringTranslator.cs @@ -34,7 +34,12 @@ public virtual Expression Translate(MethodCallExpression methodCallExpression) => methodCallExpression.Method.Name == nameof(ToString) && methodCallExpression.Arguments.Count == 0 && methodCallExpression.Object != null && - SupportedTypes.Contains(methodCallExpression.Object.Type.UnwrapNullableType().UnwrapEnumType()) + SupportedTypes.Contains( + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue9894", out var enabled) + && enabled + ? methodCallExpression.Object.Type.UnwrapNullableType().UnwrapEnumType() + : methodCallExpression.Object.Type.UnwrapNullableType() + ) ? new ExplicitCastExpression(methodCallExpression.Object, typeof(string)) : null; } diff --git a/src/EFCore.PG/Query/ExpressionVisitors/NpgsqlSqlTranslatingExpressionVisitor.cs b/src/EFCore.PG/Query/ExpressionVisitors/NpgsqlSqlTranslatingExpressionVisitor.cs index 8b052c752..ded15bfd0 100644 --- a/src/EFCore.PG/Query/ExpressionVisitors/NpgsqlSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.PG/Query/ExpressionVisitors/NpgsqlSqlTranslatingExpressionVisitor.cs @@ -40,7 +40,7 @@ protected override Expression VisitSubQuery(SubQueryExpression expression) if (properties.Count == 0) return null; var lastPropertyType = properties[properties.Count - 1].ClrType; - if (lastPropertyType.IsArray && lastPropertyType.GetArrayRank() == 1) + if (lastPropertyType.IsArray && lastPropertyType.GetArrayRank() == 1 && subQueryModel.ResultOperators.Count > 0) { // Translate someArray.Length if (subQueryModel.ResultOperators.First() is CountResultOperator) diff --git a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs index c201869b7..97ef11d03 100644 --- a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs +++ b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs @@ -163,7 +163,7 @@ void GetTables() const string GetColumnsQuery = @" SELECT - nspname, relname, attisdropped, attname, typ.typname, atttypmod, description, + nspname, relname, attisdropped, attname, typ.typname, atttypmod, description, basetyp.typname AS domtypname, CASE WHEN pg_proc.proname='array_recv' THEN 'a' ELSE typ.typtype END AS typtype, CASE WHEN pg_proc.proname='array_recv' THEN elemtyp.typname @@ -177,6 +177,7 @@ FROM pg_class AS cls LEFT OUTER JOIN pg_type AS typ ON attr.atttypid = typ.oid LEFT OUTER JOIN pg_proc ON pg_proc.oid = typ.typreceive LEFT OUTER JOIN pg_type AS elemtyp ON (elemtyp.oid = typ.typelem) +LEFT OUTER JOIN pg_type AS basetyp ON (basetyp.oid = typ.typbasetype) LEFT OUTER JOIN pg_description AS des ON des.objoid = cls.oid AND des.objsubid = attnum WHERE relkind = 'r' AND @@ -261,6 +262,9 @@ void GetColumns() case 'e': column[NpgsqlAnnotationNames.PostgresTypeType] = PostgresTypeType.Enum; break; + case 'd': + column.StoreType = GetStoreType(reader.GetValueOrDefault("domtypname"), typeModifier); + break; default: Logger.Logger.LogWarning($"Can't scaffold column '{columnName}' of type '{dataType}': unknown type char '{typeChar}'"); continue; @@ -535,7 +539,10 @@ void GetSequences() Debug.Assert(sequence.MinValue.HasValue); defaultStart = sequence.MinValue.Value; } else { - defaultMin = long.MinValue + 1; + // PostgreSQL 10 changed the default minvalue for a descending sequence, see #264 + defaultMin = _connection.PostgreSqlVersion >= new Version(10,0) + ? long.MinValue + : long.MinValue + 1; defaultMax = -1; Debug.Assert(sequence.MaxValue.HasValue); defaultStart = sequence.MaxValue.Value; diff --git a/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlDateTimeOffsetTypeMapping.cs b/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlDateTimeOffsetTypeMapping.cs new file mode 100644 index 000000000..99ca44c93 --- /dev/null +++ b/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlDateTimeOffsetTypeMapping.cs @@ -0,0 +1,22 @@ +using System.Data; +using JetBrains.Annotations; + +namespace Microsoft.EntityFrameworkCore.Storage.Internal +{ + public class NpgsqlDateTimeOffsetTypeMapping : DateTimeOffsetTypeMapping + { + private const string DateTimeOffsetFormatConst = "{0:yyyy-MM-ddTHH:mm:ss.fffzzz}"; + + public NpgsqlDateTimeOffsetTypeMapping( + [NotNull] string storeType, + [NotNull] DbType? dbType = System.Data.DbType.DateTimeOffset) + : base(storeType, dbType: dbType) + { + } + + public override RelationalTypeMapping Clone(string storeType, int? size) + => new NpgsqlDateTimeOffsetTypeMapping(storeType, DbType); + + protected override string SqlLiteralFormatString => $"'{DateTimeOffsetFormatConst}'"; + } +} diff --git a/src/EFCore.PG/Storage/Internal/NpgsqlEFTypeMapper.cs b/src/EFCore.PG/Storage/Internal/NpgsqlEFTypeMapper.cs index 4dfd79ce9..7411e66a6 100644 --- a/src/EFCore.PG/Storage/Internal/NpgsqlEFTypeMapper.cs +++ b/src/EFCore.PG/Storage/Internal/NpgsqlEFTypeMapper.cs @@ -87,7 +87,7 @@ void AddCustomizedMappings() _baseClrMappings[typeof(char)] = new CharTypeMapping("text", DbType.String); _baseClrMappings[typeof(DateTime)] = _storeTypeMappings["timestamp"] = new DateTimeTypeMapping("timestamp", DbType.DateTime); - _storeTypeMappings["timestamptz"] = new DateTimeTypeMapping("timestamptz", DbType.DateTime); + _baseClrMappings[typeof(DateTimeOffset)] = _storeTypeMappings["timestamptz"] = new NpgsqlDateTimeOffsetTypeMapping("timestamptz", DbType.DateTimeOffset); _baseClrMappings[typeof(bool)] = _storeTypeMappings["bool"] = new NpgsqlBoolTypeMapping(); _baseClrMappings[typeof(decimal)] = new DecimalTypeMapping("numeric", DbType.Decimal); diff --git a/test/EFCore.PG.Design.FunctionalTests/EFCore.PG.Design.FunctionalTests.csproj b/test/EFCore.PG.Design.FunctionalTests/EFCore.PG.Design.FunctionalTests.csproj index ffc206b0c..5fc767c5d 100644 --- a/test/EFCore.PG.Design.FunctionalTests/EFCore.PG.Design.FunctionalTests.csproj +++ b/test/EFCore.PG.Design.FunctionalTests/EFCore.PG.Design.FunctionalTests.csproj @@ -2,6 +2,7 @@ net461;netcoreapp2.0 + netcoreapp2.0 true Npgsql.EntityFrameworkCore.PostgreSQL.Design.FunctionalTests Npgsql.EntityFrameworkCore.PostgreSQL.Design.FunctionalTests @@ -19,10 +20,8 @@ - - - - + + diff --git a/test/EFCore.PG.Design.FunctionalTests/NpgsqlDatabaseModelFactoryTest.cs b/test/EFCore.PG.Design.FunctionalTests/NpgsqlDatabaseModelFactoryTest.cs index 12f9f6d9b..6f5835ef2 100644 --- a/test/EFCore.PG.Design.FunctionalTests/NpgsqlDatabaseModelFactoryTest.cs +++ b/test/EFCore.PG.Design.FunctionalTests/NpgsqlDatabaseModelFactoryTest.cs @@ -180,7 +180,7 @@ public void It_reads_primary_key() var sql = "CREATE TABLE place (id int PRIMARY KEY, name int UNIQUE, location int);" + "CREATE INDEX ix_location_name ON place (location, name);"; - var dbModel = CreateModel(sql); + var dbModel = CreateModel(sql, new List { "place" }); var pkIndex = dbModel.Tables.Single().PrimaryKey; diff --git a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/E2E.sql b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/E2E.sql index f8197b903..6fa0d410b 100644 --- a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/E2E.sql +++ b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/E2E.sql @@ -1,4 +1,8 @@ DROP TABLE IF EXISTS "AllDataTypes"; + +DROP DOMAIN IF EXISTS domain_type; +CREATE DOMAIN domain_type AS smallint CONSTRAINT domain_type_check CHECK (((VALUE >= (-50)) AND (VALUE <= 50))); + CREATE TABLE "AllDataTypes" ( "AllDataTypesID" serial PRIMARY KEY, @@ -22,7 +26,9 @@ CREATE TABLE "AllDataTypes" ( "byteaColumn" bytea NULL, "boolColumn" boolean NOT NULL, - "uuidColumn" uuid NULL + "uuidColumn" uuid NULL, + + "domainColumn" domain_type NOT NULL ); DROP TABLE IF EXISTS "PropertyConfiguration"; diff --git a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/AllDataTypes.expected b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/AllDataTypes.expected index 21fb9b737..cf5545cb1 100644 --- a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/AllDataTypes.expected +++ b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/AllDataTypes.expected @@ -19,10 +19,11 @@ namespace E2ETest.Namespace public string VarcharColumn { get; set; } public DateTime DateColumn { get; set; } public DateTime? TimestampColumn { get; set; } - public DateTime? TimestampTzColumn { get; set; } + public DateTimeOffset? TimestampTzColumn { get; set; } public TimeSpan? TimeColumn { get; set; } public byte[] ByteaColumn { get; set; } public bool BoolColumn { get; set; } public Guid? UuidColumn { get; set; } + public short DomainColumn { get; set; } } } diff --git a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/NpgsqlReverseEngineerTestE2EContext.expected b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/NpgsqlReverseEngineerTestE2EContext.expected index 07feb50d1..7c93c46c6 100644 --- a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/NpgsqlReverseEngineerTestE2EContext.expected +++ b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/AllFluentApi/NpgsqlReverseEngineerTestE2EContext.expected @@ -51,6 +51,8 @@ namespace E2ETest.Namespace entity.Property(e => e.DecimalColumn).HasColumnName("decimalColumn"); + entity.Property(e => e.DomainColumn).HasColumnName("domainColumn"); + entity.Property(e => e.DoubleColumn).HasColumnName("doubleColumn"); entity.Property(e => e.IntColumn).HasColumnName("intColumn"); @@ -73,9 +75,7 @@ namespace E2ETest.Namespace entity.Property(e => e.TimestampColumn).HasColumnName("timestampColumn"); - entity.Property(e => e.TimestampTzColumn) - .HasColumnName("timestampTzColumn") - .HasColumnType("timestamptz"); + entity.Property(e => e.TimestampTzColumn).HasColumnName("timestampTzColumn"); entity.Property(e => e.UuidColumn).HasColumnName("uuidColumn"); diff --git a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/Attributes/AllDataTypes.expected b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/Attributes/AllDataTypes.expected index 87d9f014d..f4af73a4e 100644 --- a/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/Attributes/AllDataTypes.expected +++ b/test/EFCore.PG.Design.FunctionalTests/ReverseEngineering/Expected/Attributes/AllDataTypes.expected @@ -35,8 +35,8 @@ namespace E2ETest.Namespace public DateTime DateColumn { get; set; } [Column("timestampColumn")] public DateTime? TimestampColumn { get; set; } - [Column("timestampTzColumn", TypeName = "timestamptz")] - public DateTime? TimestampTzColumn { get; set; } + [Column("timestampTzColumn")] + public DateTimeOffset? TimestampTzColumn { get; set; } [Column("timeColumn", TypeName = "time")] public TimeSpan? TimeColumn { get; set; } [Column("byteaColumn")] @@ -45,5 +45,7 @@ namespace E2ETest.Namespace public bool BoolColumn { get; set; } [Column("uuidColumn")] public Guid? UuidColumn { get; set; } + [Column("domainColumn")] + public short DomainColumn { get; set; } } } diff --git a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlFixture.cs index b13337ec1..18b8b1258 100644 --- a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlFixture.cs +++ b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlFixture.cs @@ -134,6 +134,10 @@ public override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().Property(e => e.Xid).HasColumnType("xid"); modelBuilder.Entity().Property(e => e.Xid).HasColumnType("xid"); + + // TimeTz + modelBuilder.Entity().Property(e => e.Timetz).HasColumnType("timetz"); + modelBuilder.Entity().Property(e => e.Timetz).HasColumnType("timetz"); } private static void MapColumnTypes(ModelBuilder modelBuilder) where TEntity : class @@ -197,10 +201,10 @@ public class MappedDataTypes public byte[] Bytea { get; set; } public DateTime Timestamp { get; set; } - //public DateTime Timestamptz { get; set; } + public DateTime Timestamptz { get; set; } public DateTime Date { get; set; } public TimeSpan Time { get; set; } - //public DateTimeOffset Timetz { get; set; } + public DateTimeOffset Timetz { get; set; } public TimeSpan Interval { get; set; } public Guid Uuid { get; set; } diff --git a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs index 1c6eff2d5..e1569f903 100644 --- a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs @@ -36,10 +36,10 @@ public virtual void Can_query_using_any_mapped_data_type() Bytea = new byte[] { 86 }, Timestamp = new DateTime(2015, 1, 2, 10, 11, 12), - //Timestamptz = new DateTime(2016, 1, 2, 11, 11, 12, DateTimeKind.Utc), + Timestamptz = new DateTime(2016, 1, 2, 11, 11, 12, DateTimeKind.Utc), Date = new DateTime(2015, 1, 2, 0, 0, 0), Time = new TimeSpan(11, 15, 12), - //Timetz = new DateTimeOffset(0, 0, 0, 12, 0, 0, TimeSpan.FromHours(2)), + Timetz = new DateTimeOffset(1, 1, 1, 12, 0, 0, TimeSpan.FromHours(2)), Interval = new TimeSpan(11, 15, 12), Uuid = new Guid("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"), @@ -95,8 +95,8 @@ public virtual void Can_query_using_any_mapped_data_type() DateTime? param10 = new DateTime(2015, 1, 2, 10, 11, 12); Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Timestamp == param10)); - //DateTime? param11 = new DateTime(2019, 1, 2, 14, 11, 12, DateTimeKind.Utc); - //Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Timestamptz == param11)); + DateTime? param11 = new DateTime(2016, 1, 2, 11, 11, 12, DateTimeKind.Utc); + Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Timestamptz == param11)); DateTime? param12 = new DateTime(2015, 1, 2, 0, 0, 0); Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Date == param12)); @@ -104,8 +104,8 @@ public virtual void Can_query_using_any_mapped_data_type() TimeSpan? param13 = new TimeSpan(11, 15, 12); Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Time == param13)); - //DateTimeOffset? param14 = new DateTimeOffset(0, 0, 0, 12, 0, 0, TimeSpan.FromHours(2)); - //Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Timetz == param14)); + DateTimeOffset? param14 = new DateTimeOffset(1, 1, 1, 12, 0, 0, TimeSpan.FromHours(2)); + Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Timetz == param14)); TimeSpan? param15 = new TimeSpan(11, 15, 12); Assert.Same(entity, context.Set().Single(e => e.Int == 999 && e.Interval == param15)); @@ -259,10 +259,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types() Bytea = new byte[] { 86 }, Timestamp = new DateTime(2016, 1, 2, 11, 11, 12), - //Timestamptz = new DateTime(2016, 1, 2, 11, 11, 12, DateTimeKind.Utc), + Timestamptz = new DateTime(2016, 1, 2, 11, 11, 12, DateTimeKind.Utc), Date = new DateTime(2015, 1, 2, 10, 11, 12), Time = new TimeSpan(11, 15, 12), - //Timetz = new DateTimeOffset(0, 0, 0, 12, 0, 0, TimeSpan.FromHours(2)), + Timetz = new DateTimeOffset(1, 1, 1, 12, 0, 0, TimeSpan.FromHours(2)), Interval = new TimeSpan(11, 15, 12), Uuid = new Guid("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"), @@ -301,10 +301,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types() Assert.Equal(new byte[] { 86 }, entity.Bytea); Assert.Equal(new DateTime(2016, 1, 2, 11, 11, 12), entity.Timestamp); - //Assert.Equal(new DateTime(2016, 1, 2, 11, 11, 12), entity.Timestamptz); + Assert.Equal(new DateTime(2016, 1, 2, 11, 11, 12), entity.Timestamptz); Assert.Equal(new DateTime(2015, 1, 2, 0, 0, 0), entity.Date); Assert.Equal(new TimeSpan(11, 15, 12), entity.Time); - //Assert.Equal(new DateTimeOffset(0, 0, 0, 12, 0, 0, TimeSpan.FromHours(2)), entity.Timetz); + Assert.Equal(new DateTimeOffset(1, 1, 1, 12, 0, 0, TimeSpan.FromHours(2)), entity.Timetz); Assert.Equal(new TimeSpan(11, 15, 12), entity.Interval); Assert.Equal(new Guid("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"), entity.Uuid); @@ -343,10 +343,10 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types() Bytea = new byte[] { 86 }, Timestamp = new DateTime(2016, 1, 2, 11, 11, 12), - //Timestamptz = new DateTime(2016, 1, 2, 11, 11, 12, DateTimeKind.Utc), + Timestamptz = new DateTime(2016, 1, 2, 11, 11, 12, DateTimeKind.Utc), Date = new DateTime(2015, 1, 2, 10, 11, 12), Time = new TimeSpan(11, 15, 12), - //Timetz = new DateTimeOffset(0, 0, 0, 12, 0, 0, TimeSpan.FromHours(2)), + Timetz = new DateTimeOffset(1, 1, 1, 12, 0, 0, TimeSpan.FromHours(2)), Interval = new TimeSpan(11, 15, 12), Uuid = new Guid("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"), @@ -383,10 +383,10 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types() Assert.Equal(new byte[] { 86 }, entity.Bytea); Assert.Equal(new DateTime(2016, 1, 2, 11, 11, 12), entity.Timestamp); - //Assert.Equal(new DateTime(2016, 1, 2, 11, 11, 12), entity.Timestamptz); + Assert.Equal(new DateTime(2016, 1, 2, 11, 11, 12), entity.Timestamptz); Assert.Equal(new DateTime(2015, 1, 2, 0, 0, 0), entity.Date); Assert.Equal(new TimeSpan(11, 15, 12), entity.Time); - //Assert.Equal(new DateTimeOffset(0, 0, 0, 12, 0, 0, TimeSpan.FromHours(2)), entity.Timetz); + Assert.Equal(new DateTimeOffset(1, 1, 1, 12, 0, 0, TimeSpan.FromHours(2)), entity.Timetz); Assert.Equal(new TimeSpan(11, 15, 12), entity.Interval); Assert.Equal(new Guid("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"), entity.Uuid); diff --git a/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj b/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj index a89a387f7..66fc5daa5 100644 --- a/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj +++ b/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj @@ -2,6 +2,7 @@ net461;netcoreapp2.0 + netcoreapp2.0 Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests xUnit1004;xUnit1013 @@ -22,17 +23,15 @@ - - - - - - - + + + + + + - diff --git a/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsQueryNpgsqlTest.cs index ed8eef879..b4ddf4403 100644 --- a/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsQueryNpgsqlTest.cs @@ -30,5 +30,8 @@ public override void Query_source_materialization_bug_4547() {} [Fact(Skip = "PostgreSQL sorts nulls first (#50)")] public override void Optional_navigation_take_optional_navigation() {} + + [Fact(Skip = "PostgreSQL sorts nulls first (#50)")] + public override void Include_reference_collection_order_by_reference_navigation() { } } } diff --git a/test/EFCore.PG.FunctionalTests/Query/QueryBugTest.cs b/test/EFCore.PG.FunctionalTests/Query/QueryBugTest.cs new file mode 100644 index 000000000..5bd2cecb1 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/QueryBugTest.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Utilities; +using Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.EntityFrameworkCore.Query +{ + public class QueryBugsTest : IClassFixture + { + // ReSharper disable once UnusedParameter.Local + public QueryBugsTest(NpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + { + Fixture = fixture; + //Fixture.TestSqlLoggerFactory.Clear(); + //Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected NpgsqlFixture Fixture { get; } + + [Fact] + public async Task Bug278() + { + using (var testStore = NpgsqlTestStore.CreateScratch()) + using (var context = new Bug278Context(new DbContextOptionsBuilder() + .UseNpgsql(testStore.Connection) + .Options)) + { + context.Database.EnsureCreated(); + context.Entities.Add(new Bug278Entity { ChannelCodes = new[] { 1, 1 } }); + context.SaveChanges(); + + var actual = await context.Entities.Select(x => new + { + Codes = x.ChannelCodes.Select(c => (ChannelCode)c) + }).FirstOrDefaultAsync(); + + Assert.Equal(new[] { ChannelCode.Code, ChannelCode.Code }, actual.Codes); + } + } + + public enum ChannelCode { Code = 1 } + + public class Bug278Entity + { + public int Id { get; set; } + public int[] ChannelCodes { get; set; } + } + + class Bug278Context : DbContext + { + public Bug278Context(DbContextOptions options) : base(options) {} + public DbSet Entities { get; set; } + } + } +} diff --git a/test/EFCore.PG.FunctionalTests/Query/QueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/QueryNpgsqlTest.cs index 98023c3bb..1bb6f1fab 100644 --- a/test/EFCore.PG.FunctionalTests/Query/QueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/QueryNpgsqlTest.cs @@ -21,6 +21,9 @@ public override void OrderBy_coalesce_skip_take_distinct_take() { } [Fact(Skip = "Support in PG only for numerics")] public override void Where_math_log_new_base() { } + [Fact(Skip = "https://github.com/aspnet/EntityFrameworkCore/pull/10352")] + public override void DefaultIfEmpty_in_subquery_nested() {} + #region Inherited public override void String_Contains_Literal() diff --git a/test/EFCore.PG.Tests/EFCore.PG.Tests.csproj b/test/EFCore.PG.Tests/EFCore.PG.Tests.csproj index 395f1710b..73de380dc 100644 --- a/test/EFCore.PG.Tests/EFCore.PG.Tests.csproj +++ b/test/EFCore.PG.Tests/EFCore.PG.Tests.csproj @@ -2,6 +2,7 @@ net461;netcoreapp2.0 + netcoreapp2.0 Npgsql.EntityFrameworkCore.PostgreSQL.Tests Npgsql.EntityFrameworkCore.PostgreSQL.Tests @@ -12,13 +13,11 @@ - - - - - - - + + + + + diff --git a/test/EFCore.PG.Tests/NpgsqlDbFunctionsTest.cs b/test/EFCore.PG.Tests/NpgsqlDbFunctionsTest.cs new file mode 100644 index 000000000..9162468c2 --- /dev/null +++ b/test/EFCore.PG.Tests/NpgsqlDbFunctionsTest.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +// ReSharper disable InconsistentNaming + +namespace Microsoft.EntityFrameworkCore +{ + public class NpgsqlDbFunctionsTest + { + readonly DbFunctions _functions = EF.Functions; + + [Fact] + public void ILike_when_no_wildcards() + { + Assert.True(_functions.ILike("abc", "abc")); + Assert.True(_functions.ILike("abc", "ABC")); + Assert.True(_functions.ILike("ABC", "abc")); + + Assert.False(_functions.ILike("ABC", "ab")); + Assert.False(_functions.ILike("ab", "abc")); + } + } +}