diff --git a/Changelog.md b/Changelog.md index d1b11f9..4d85475 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,21 +1,10 @@ # NEventStore.Persistence.Sql -## PostgreSQL Warning - -If you upgrade Npgsql to version 6.0 and up you must take into account the breaking changes -made about the timezones handling [Timestamp rationalization and improvements](https://www.npgsql.org/efcore/release-notes/6.0.html#timestamp-rationalization-and-improvements). - -Possible solutions: -- manually migrate the Table schema and update the "CommitStamp" column type from "timestamp" to "timestamptz". -- disable the new behavior by calling: - ``` - AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); - ``` - ## 9.0.0 - Updated NEventStore core library to 9.0.0. - Added support for net6.0. +- Added a new PostgreSQL dialect (`PostgreNpgsql6Dialect`) to deal with Npgsql version 6 timestamp breaking changes. If you update the driver you might need to update the table schema manually [#34](https://github.com/NEventStore/NEventStore.Persistence.SQL/issues/34). ## 8.0.0 diff --git a/README.md b/README.md index 5669f81..ee0943b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ NEventStore.Persistence.Sql currently supports: - .net framework 4.6.1 - .net standard 2.0 - .net 5.0 +- .net 6.0 - MsSql - SqlLite - MySql @@ -22,6 +23,19 @@ Branches: - master [![Build status](https://ci.appveyor.com/api/projects/status/5difan7hap8vwhwe/branch/master?svg=true)](https://ci.appveyor.com/project/AGiorgetti/neventstore-persistence-sql/branch/master) - develop [![Build status](https://ci.appveyor.com/api/projects/status/5difan7hap8vwhwe/branch/develop?svg=true)](https://ci.appveyor.com/project/AGiorgetti/neventstore-persistence-sql/branch/develop) +## PostgreSQL Warning + +If you upgrade Npgsql to version 6.0 and up you must take into account the breaking changes +made about the timezones handling [Timestamp rationalization and improvements](https://www.npgsql.org/efcore/release-notes/6.0.html#timestamp-rationalization-and-improvements). + +Possible solutions: +- manually migrate the Table schema and update the "CommitStamp" column type from "timestamp" to "timestamptz". +- disable the new behavior by calling: + ``` + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + ``` +- use the new `PostgreNpgsql6Dialect`. + ## How to Build (locally) - Clone the repository with: diff --git a/dependencies/NEventStore b/dependencies/NEventStore index b0d34c1..34ad7c0 160000 --- a/dependencies/NEventStore +++ b/dependencies/NEventStore @@ -1 +1 @@ -Subproject commit b0d34c1805355f8de9af645705ded08701a83a12 +Subproject commit 34ad7c0fa565d4b5e1548105942579cccc7b00be diff --git a/src/NEventStore.Persistence.PostgreSql.Tests/NEventStore.Persistence.PostgreSql.Core.Tests.csproj b/src/NEventStore.Persistence.PostgreSql.Tests/NEventStore.Persistence.PostgreSql.Core.Tests.csproj index 12a8ec6..6cfdf97 100644 --- a/src/NEventStore.Persistence.PostgreSql.Tests/NEventStore.Persistence.PostgreSql.Core.Tests.csproj +++ b/src/NEventStore.Persistence.PostgreSql.Tests/NEventStore.Persistence.PostgreSql.Core.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/NEventStore.Persistence.PostgreSql.Tests/PersistenceEngineFixture.cs b/src/NEventStore.Persistence.PostgreSql.Tests/PersistenceEngineFixture.cs index 5cbc8e9..45ef4e1 100644 --- a/src/NEventStore.Persistence.PostgreSql.Tests/PersistenceEngineFixture.cs +++ b/src/NEventStore.Persistence.PostgreSql.Tests/PersistenceEngineFixture.cs @@ -5,24 +5,28 @@ namespace NEventStore.Persistence.AcceptanceTests using NEventStore.Persistence.Sql; using NEventStore.Persistence.Sql.SqlDialects; using NEventStore.Serialization; + using System; public partial class PersistenceEngineFixture { public PersistenceEngineFixture() { + // It will be done when creating the PostgreNpgsql6Dialect dialect + // AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + #if NET461 _createPersistence = pageSize => new SqlPersistenceFactory( new EnviromentConnectionFactory("PostgreSql", "Npgsql"), new BinarySerializer(), - new PostgreSqlDialect(), + new PostgreNpgsql6Dialect(npgsql6timestamp: true), pageSize: pageSize).Build(); #else _createPersistence = pageSize => new SqlPersistenceFactory( new EnviromentConnectionFactory("PostgreSql", Npgsql.NpgsqlFactory.Instance), new BinarySerializer(), - new PostgreSqlDialect(), + new PostgreNpgsql6Dialect(npgsql6timestamp: true), pageSize: pageSize).Build(); #endif } diff --git a/src/NEventStore.Persistence.Sql/SqlDialects/PostgreSqlDialect.cs b/src/NEventStore.Persistence.Sql/SqlDialects/PostgreSqlDialect.cs index ca77ab2..a74a327 100644 --- a/src/NEventStore.Persistence.Sql/SqlDialects/PostgreSqlDialect.cs +++ b/src/NEventStore.Persistence.Sql/SqlDialects/PostgreSqlDialect.cs @@ -1,6 +1,7 @@ namespace NEventStore.Persistence.Sql.SqlDialects { using System; + using System.Data; public class PostgreSqlDialect : CommonSqlDialect { @@ -20,4 +21,61 @@ public override bool IsDuplicate(Exception exception) return message.Contains("23505") || message.Contains("IX_COMMITS_COMMITSEQUENCE"); } } + + public class PostgreNpgsql6Dialect : CommonSqlDialect + { + private readonly bool _npgsql6Timestamp; + + /// + /// Create an instance of the PostgreSQL dialect + /// + /// + /// There's a breaking change in Npgsql Version 6.x that changes the way timestamps should be persisted. + /// - false: to disable the new behavior for the whole application (see the driver release notes) this should be done before using the database. + /// - true: enable the new behavior, you might need to migrate the data manually (see Npgsql release notes). + /// + public PostgreNpgsql6Dialect( + bool npgsql6timestamp = true + ) + { + _npgsql6Timestamp = npgsql6timestamp; + if (!_npgsql6Timestamp) + { + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + } + } + + public override string InitializeStorage + { + get + { + var initStorage = PostgreSqlStatements.InitializeStorage; + if (_npgsql6Timestamp) + { + initStorage = initStorage.Replace("timestamp", "timestamptz"); + } + return initStorage; + } + } + + public override string PersistCommit + { + get { return PostgreSqlStatements.PersistCommits; } + } + + public override bool IsDuplicate(Exception exception) + { + string message = exception.Message.ToUpperInvariant(); + return message.Contains("23505") || message.Contains("IX_COMMITS_COMMITSEQUENCE"); + } + + public override DbType GetDateTimeDbType() + { + if (_npgsql6Timestamp) + { + return DbType.DateTimeOffset; + } + return base.GetDateTimeDbType(); + } + } } \ No newline at end of file diff --git a/src/NEventStore.Persistence.Sql/SqlPersistenceEngine.cs b/src/NEventStore.Persistence.Sql/SqlPersistenceEngine.cs index 9025a28..a7b0d5d 100644 --- a/src/NEventStore.Persistence.Sql/SqlPersistenceEngine.cs +++ b/src/NEventStore.Persistence.Sql/SqlPersistenceEngine.cs @@ -13,7 +13,7 @@ namespace NEventStore.Persistence.Sql public class SqlPersistenceEngine : IPersistStreams { private static readonly ILogger Logger = LogFactory.BuildLogger(typeof(SqlPersistenceEngine)); - private static readonly DateTime EpochTime = new DateTime(1970, 1, 1); + private static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private readonly IConnectionFactory _connectionFactory; private readonly ISqlDialect _dialect; private readonly int _pageSize;