diff --git a/doc/reference/modules/basic_mapping.xml b/doc/reference/modules/basic_mapping.xml index 42c7c23de78..75a6eb62ef6 100644 --- a/doc/reference/modules/basic_mapping.xml +++ b/doc/reference/modules/basic_mapping.xml @@ -2638,14 +2638,30 @@ DateTime System.DateTime - DbType.DateTime - ignores the milliseconds - Default when no type attribute specified. + + DbType.DateTime / DbType.DateTime2 + + + Default when no type attribute specified. Does no + more ignore milliseconds since NHibernate v5.0. + + + + DateTimeNoMs + System.DateTime + + DbType.DateTime / DbType.DateTime2 + + type="DateTimeNoMs" must be specified. Ignores milliseconds. DateTime2 System.DateTime DbType.DateTime2 - type="DateTime2" must be specified. + + type="DateTime2" must be specified. Obsolete since + NHibernate v5.0, use DateTime instead. + DateTimeOffset @@ -2656,9 +2672,14 @@ DbTimestamp System.DateTime - DbType.DateTime - as specific as database supports. - type="DbTimestamp" must be specified. When used as a version - field, uses the database's current time rather than the client's current time. + + DbType.DateTime / DbType.DateTime2 + + + type="DbTimestamp" must be specified. When used as a + version field, uses the database's current time retrieved + in dedicated queries, rather than the client's current time. + Decimal @@ -2699,9 +2720,26 @@ LocalDateTime System.DateTime - DbType.DateTime - ignores the milliseconds - type="LocalDateTime" must be specified. Ensures the - DateTimeKind is set to DateTimeKind.Local + + DbType.DateTime / DbType.DateTime2 + + + type="LocalDateTime" must be specified. Ensures the + DateTimeKind is set to DateTimeKind.Local. + Throws if set with a date having another kind. + Does no more ignore milliseconds since NHibernate v5.0. + + + + LocalDateTimeNoMs + System.DateTime + + DbType.DateTime / DbType.DateTime2 + + + type="LocalDateTimeNoMs" must be specified. Similar to + type="LocalDateTime" but ignores milliseconds. + PersistentEnum @@ -2750,15 +2788,13 @@ Timestamp System.DateTime - DbType.DateTime - as specific as database supports. - type="Timestamp" must be specified. - - - TimestampUtc - System.DateTime - DbType.DateTime - as specific as database supports. - type="TimestampUtc" must be specified. Ensures the - DateTimeKind is set to DateTimeKind.Utc + + DbType.DateTime / DbType.DateTime2 + + + Obsolete, its Timestamp alias will be remapped to + DateTime in a future version. + TrueFalse @@ -2787,8 +2823,25 @@ UtcDateTime System.DateTime - DbType.DateTime - ignores the milliseconds - Ensures the DateTimeKind is set to DateTimeKind.Utc + + DbType.DateTime / DbType.DateTime2 + + + Ensures the DateTimeKind is set to DateTimeKind.Utc. + Throws if set with a date having another kind. + Does no more ignore milliseconds since NHibernate v5.0. + + + + UtcDateTimeNoMs + System.DateTime + + DbType.DateTime / DbType.DateTime2 + + + type="UtcDateTimeNoMs" must be specified. Similar to + type="LocalDateTime" but ignores milliseconds. + YesNo @@ -2800,6 +2853,16 @@ + + + + Since NHibernate v5.0 and if the dialect supports it, DbType.DateTime2 + is used instead of DbType.DateTime. This may be disabled by setting + sql_types.keep_datetime to true. + + + + System.Object Mapping Types diff --git a/src/NHibernate.Test/Async/Legacy/CriteriaTest.cs b/src/NHibernate.Test/Async/Legacy/CriteriaTest.cs index 2bd3c90bc89..b7cc9f1b060 100644 --- a/src/NHibernate.Test/Async/Legacy/CriteriaTest.cs +++ b/src/NHibernate.Test/Async/Legacy/CriteriaTest.cs @@ -40,14 +40,14 @@ public async Task SimpleSelectTestAsync() long simple1Key = 15; Simple simple1 = new Simple(); simple1.Address = "Street 12"; - simple1.Date = DateTime.Now; + simple1.Date = RoundForDialect(DateTime.Now); simple1.Name = "For Criteria Test"; simple1.Count = 16; long notSimple1Key = 17; Simple notSimple1 = new Simple(); notSimple1.Address = "Street 123"; - notSimple1.Date = DateTime.Now; + notSimple1.Date = RoundForDialect(DateTime.Now); notSimple1.Name = "Don't be found"; notSimple1.Count = 18; @@ -62,19 +62,19 @@ public async Task SimpleSelectTestAsync() using (ISession s2 = OpenSession()) using (ITransaction t2 = s2.BeginTransaction()) { - IList results2 = await (s2.CreateCriteria(typeof(Simple)) - .Add(Expression.Eq("Address", "Street 12")) - .ListAsync()); + var results2 = await (s2.CreateCriteria() + .Add(Restrictions.Eq("Address", "Street 12")) + .ListAsync()); - Assert.AreEqual(1, results2.Count); + Assert.That(results2.Count, Is.EqualTo(1), "Unexpected result count"); - Simple simple2 = (Simple) results2[0]; + var simple2 = results2[0]; - Assert.IsNotNull(simple2, "Unable to load object"); - Assert.AreEqual(simple1.Count, simple2.Count, "Load failed"); - Assert.AreEqual(simple1.Name, simple2.Name, "Load failed"); - Assert.AreEqual(simple1.Address, simple2.Address, "Load failed"); - Assert.AreEqual(simple1.Date.ToString(), simple2.Date.ToString(), "Load failed"); + Assert.That(simple2, Is.Not.Null, "Unable to load object"); + Assert.That(simple2.Count, Is.EqualTo(simple1.Count), "Unexpected Count property value"); + Assert.That(simple2.Name, Is.EqualTo(simple1.Name), "Unexpected name"); + Assert.That(simple2.Address, Is.EqualTo(simple1.Address), "Unexpected address"); + Assert.That(simple2.Date, Is.EqualTo(simple1.Date), "Unexpected date"); await (s2.DeleteAsync("from Simple")); @@ -181,4 +181,4 @@ public async Task CriteriaLeftOuterJoinAsync() } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/Legacy/SQLLoaderTest.cs b/src/NHibernate.Test/Async/Legacy/SQLLoaderTest.cs index 30a70a2206d..cd57d25d841 100644 --- a/src/NHibernate.Test/Async/Legacy/SQLLoaderTest.cs +++ b/src/NHibernate.Test/Async/Legacy/SQLLoaderTest.cs @@ -60,7 +60,7 @@ public async Task TSAsync() await (session.SaveAsync(sim, 1L)); IQuery q = session.CreateSQLQuery("select {sim.*} from Simple {sim} where {sim}.date_ = ?") .AddEntity("sim", typeof(Simple)); - q.SetTimestamp(0, sim.Date); + q.SetDateTime(0, sim.Date); Assert.AreEqual(1, (await (q.ListAsync())).Count, "q.List.Count"); await (session.DeleteAsync(sim)); await (txn.CommitAsync()); @@ -84,7 +84,7 @@ public async Task TSNamedAsync() IQuery q = session.CreateSQLQuery("select {sim.*} from Simple {sim} where {sim}.date_ = :fred") .AddEntity("sim", typeof(Simple)); - q.SetTimestamp("fred", sim.Date); + q.SetDateTime("fred", sim.Date); Assert.AreEqual(1, (await (q.ListAsync())).Count, "q.List.Count"); await (session.DeleteAsync(sim)); await (txn.CommitAsync()); @@ -660,4 +660,4 @@ public async Task NamedSQLQueryAsync() s.Close(); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH1756/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH1756/Fixture.cs index 0bbdb99c932..ae654b85b77 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH1756/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH1756/Fixture.cs @@ -28,19 +28,19 @@ protected override bool AppliesTo(Dialect.Dialect dialect) protected override bool AppliesTo(ISessionFactoryImplementor factory) { // ODBC driver DateTime handling with SQL Server 2008+ Client is broken and forbids using it as a time stamp - // generated on db-side. + // custom generated on db-side. // Due to NH-3895, we have to force the scale on date-time parameters to 3 (3 significant fractional seconds digits) // when using ODBC + SQL Server 2008+, otherwise DateTime values having milliseconds will be rejected. But the SQL // Server DateTime does not have actually a one millisecond resolution (it has 3.333), causing ODBC to convert the - // parameter to DateTime2. A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is + // parameter to DateTime2 (yes it supports it, only its .Net classes do not). + // A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is // to be transmitted as having 3ms or 7ms and will match if transmitted as a DateTime. But when transmitted as // DateTime2, it will no more be considered equal, causing the test to be flaky and failing two thirds of tries. // Example failing update captured with profiler: // exec sp_executesql N'UPDATE book SET name_column = @P1 WHERE id = @P2 AND version_column = @P3', // N'@P1 nvarchar(18),@P2 int,@P3 datetime2',N'modified test book',1,'2017-08-02 16:37:16.0630000' // Setting the scale to 2 still causes failure for two thirds of tries, due to 3ms/7ms being truncated in such case - // with ODBC and SQL Server 2008+ Client, which is rejected bu ODBC. - // (Affects DbVersionFixture too.) + // with ODBC and SQL Server 2008+ Client, which is rejected by ODBC. return !(factory.ConnectionProvider.Driver is OdbcDriver); } @@ -115,4 +115,4 @@ public async Task SaveTransient_Then_Update_BugAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs index 7eb035a4d54..e430f2909e9 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs @@ -103,7 +103,7 @@ protected override HbmMapping GetMappings() rc.Property(x => x.Name); rc.Property(x => x.DateOfBirth, pm => { - pm.Type(NHibernateUtil.Timestamp); + pm.Type(NHibernateUtil.DateTime); }); }); diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3818/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3818/Fixture.cs index 0f40de8758d..f657909653b 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3818/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3818/Fixture.cs @@ -10,24 +10,19 @@ using System; using System.Linq; -using System.Linq.Dynamic; -using NHibernate.Linq; using NUnit.Framework; +using NHibernate.Linq; namespace NHibernate.Test.NHSpecificTest.NH3818 { - using System.Threading.Tasks; - [TestFixture] - public class FixtureAsync : BugTestCase - { - public override string BugNumber - { - get { return "NH3818"; } - } - + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { [Test] public async Task SelectConditionalValuesTestAsync() { + var now = RoundForDialect(DateTime.Now); using (var spy = new SqlLogSpy()) using (var session = OpenSession()) using (session.BeginTransaction()) @@ -37,7 +32,7 @@ public async Task SelectConditionalValuesTestAsync() var cat = new MyLovelyCat { GUID = Guid.NewGuid(), - Birthdate = DateTime.Now.AddDays(-days), + Birthdate = now.AddDays(-days), Color = "Black", Name = "Kitty", Price = 0 @@ -51,31 +46,33 @@ public async Task SelectConditionalValuesTestAsync() .Select(o => new { o.Color, - AliveDays = (int)(DateTime.Now - o.Birthdate).TotalDays, + AliveDays = (now - o.Birthdate).TotalDays, o.Name, o.Price, }) .SingleAsync()); //Console.WriteLine(spy.ToString()); - Assert.That(catInfo.AliveDays == days); + // We need a tolerance: we are diffing dates as a timespan instead of counting days boundaries, + // yielding a float. TimeSpan.Days yould always truncate a resulting partial day, so do not use it. + Assert.That(catInfo.AliveDays, Is.EqualTo(days).Within(0.1)); var catInfo2 = await (session.Query() .Select(o => new { o.Color, - AliveDays = o.Price > 0 ? (DateTime.Now - o.Birthdate).TotalDays : 0, + AliveDays = o.Price > 0 ? (now - o.Birthdate).TotalDays : 0, o.Name, o.Price, }) .SingleAsync()); //Console.WriteLine(spy.ToString()); - Assert.That(catInfo2.AliveDays == 0); + Assert.That(catInfo2.AliveDays, Is.EqualTo(0)); } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH392/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH392/Fixture.cs index 05a33de5326..668d78569cf 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH392/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH392/Fixture.cs @@ -46,15 +46,15 @@ public async Task UnsavedMinusOneNoNullReferenceExceptionAsync() } } - protected override void OnTearDown() + protected override void OnTearDown() { using (ISession s = Sfi.OpenSession()) { // s.Delete("from UnsavedValueMinusOne") loads then delete entities one by one, checking the version. - // This fails with ODBC & Sql Server 2008+, see NH-1756 test case or DbVersionFixture for more details. + // This fails with ODBC & Sql Server 2008+, see NH-1756 test case for more details. // Use an in-db query instead. s.CreateQuery("delete from UnsavedValueMinusOne").ExecuteUpdate(); } - } + } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3961/DateParametersComparedTo.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3961/DateParametersComparedTo.cs index e5c045d4451..d2340d2098d 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3961/DateParametersComparedTo.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3961/DateParametersComparedTo.cs @@ -114,6 +114,7 @@ public async Task NonNullableMappedAsDateTimeShouldBeCultureAgnosticAsync() // Non-reg test case [Test] + [Obsolete] public async Task NonNullableMappedAsTimestampShouldBeCultureAgnosticAsync() { using (ISession session = OpenSession()) @@ -227,6 +228,7 @@ public async Task NullableMappedAsDateTimeShouldBeCultureAgnosticAsync() // Failing test case till NH-3961 is fixed [Test] + [Obsolete] public async Task NullableMappedAsTimestampShouldBeCultureAgnosticAsync() { using (ISession session = OpenSession()) @@ -290,4 +292,4 @@ public async Task NullableShouldBeCultureAgnosticAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3984/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3984/Fixture.cs new file mode 100644 index 00000000000..d0b93d9c2d7 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3984/Fixture.cs @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using System.Linq; +using NHibernate.Dialect; +using NUnit.Framework; +using NHibernate.Linq; + +namespace NHibernate.Test.NHSpecificTest.NH3984 +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override void OnTearDown() + { + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateSQLQuery("delete from LogEntry").ExecuteUpdate(); + tx.Commit(); + } + } + + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return dialect is MsSql2008Dialect; + } + + [Test] + public async Task ShouldBePossibleToReadMillisecondsFromDatetime2ViaCreateSQLQueryAsync() + { + var now = DateTime.UtcNow; + + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var entry = new LogEntry { Text = "Test", CreatedAt = now }; + await (session.SaveAsync(entry)); + + await (session.FlushAsync()); + await (transaction.CommitAsync()); + } + + using (var session = OpenSession()) + { + var datetimeViaQuery = await (session + .Query() + .Select(x => x.CreatedAt) + .SingleAsync()); + + Assert.That(datetimeViaQuery.Millisecond, Is.EqualTo(now.Millisecond)); + + var datetimeViaCreateSqlQuery = (await (session + .CreateSQLQuery("SELECT CreatedAt FROM LogEntry") + .ListAsync())) + .Cast() + .Single(); + + Assert.That(datetimeViaCreateSqlQuery.Millisecond, Is.EqualTo(now.Millisecond)); + } + } + + [Test] + public async Task ShouldBePossibleToReadTicksFromDatetime2ViaCreateSQLQueryAsync() + { + var now = DateTime.Now; + + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var entry = new LogEntry { Text = "Test", CreatedAt = now }; + await (session.SaveAsync(entry)); + + await (session.FlushAsync()); + await (transaction.CommitAsync()); + } + + using (var session = OpenSession()) + { + var datetimeViaQuery = await (session + .Query() + .Select(x => x.CreatedAt) + .SingleAsync()); + + Assert.That(datetimeViaQuery.Ticks, Is.EqualTo(now.Ticks)); + + var datetimeViaCreateSqlQuery = (await (session + .CreateSQLQuery("SELECT CreatedAt FROM LogEntry") + .ListAsync())) + .Cast() + .Single(); + + Assert.That(datetimeViaCreateSqlQuery.Ticks, Is.EqualTo(now.Ticks)); + } + } + } +} diff --git a/src/NHibernate.Test/Async/Operations/MergeFixture.cs b/src/NHibernate.Test/Async/Operations/MergeFixture.cs index dfcf06ba22e..f8864afcde5 100644 --- a/src/NHibernate.Test/Async/Operations/MergeFixture.cs +++ b/src/NHibernate.Test/Async/Operations/MergeFixture.cs @@ -8,6 +8,7 @@ //------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using NHibernate.Criterion; using NUnit.Framework; @@ -189,9 +190,21 @@ public async Task MergeDeepTreeAsync() { ClearCounts(); - var root = new Node {Name = "root"}; - var child = new Node {Name = "child"}; - var grandchild = new Node {Name = "grandchild"}; + var root = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "root" + }; + var child = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child" + }; + var grandchild = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "grandchild" + }; using (ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) @@ -207,7 +220,11 @@ public async Task MergeDeepTreeAsync() ClearCounts(); grandchild.Description = "the grand child"; - var grandchild2 = new Node {Name = "grandchild2"}; + var grandchild2 = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "grandchild2" + }; child.AddChild(grandchild2); using (var s = OpenSession()) @@ -221,8 +238,16 @@ public async Task MergeDeepTreeAsync() AssertUpdateCount(1); ClearCounts(); - var child2 = new Node {Name = "child2"}; - var grandchild3 = new Node {Name = "grandchild3"}; + var child2 = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child2" + }; + var grandchild3 = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "grandchild3" + }; child2.AddChild(grandchild3); root.AddChild(child2); @@ -261,9 +286,9 @@ public async Task MergeDeepTreeWithGeneratedIdAsync() using (ISession s = OpenSession()) { ITransaction tx = s.BeginTransaction(); - root = new NumberedNode("root"); - child = new NumberedNode("child"); - grandchild = new NumberedNode("grandchild"); + root = new NumberedNode("root", RoundForDialect(DateTime.Now)); + child = new NumberedNode("child", RoundForDialect(DateTime.Now)); + grandchild = new NumberedNode("grandchild", RoundForDialect(DateTime.Now)); root.AddChild(child); child.AddChild(grandchild); root = (NumberedNode) await (s.MergeAsync(root)); @@ -281,7 +306,7 @@ public async Task MergeDeepTreeWithGeneratedIdAsync() cit.MoveNext(); grandchild = cit.Current; grandchild.Description = "the grand child"; - var grandchild2 = new NumberedNode("grandchild2"); + var grandchild2 = new NumberedNode("grandchild2", RoundForDialect(DateTime.Now)); child.AddChild(grandchild2); using (ISession s = OpenSession()) @@ -297,8 +322,8 @@ public async Task MergeDeepTreeWithGeneratedIdAsync() await (Sfi.EvictAsync(typeof (NumberedNode))); - var child2 = new NumberedNode("child2"); - var grandchild3 = new NumberedNode("grandchild3"); + var child2 = new NumberedNode("child2", RoundForDialect(DateTime.Now)); + var grandchild3 = new NumberedNode("grandchild3", RoundForDialect(DateTime.Now)); child2.AddChild(grandchild3); root.AddChild(child2); @@ -331,7 +356,7 @@ public async Task MergeManagedAsync() NumberedNode root; using (ITransaction tx = s.BeginTransaction()) { - root = new NumberedNode("root"); + root = new NumberedNode("root", RoundForDialect(DateTime.Now)); await (s.PersistAsync(root)); await (tx.CommitAsync()); } @@ -340,7 +365,7 @@ public async Task MergeManagedAsync() NumberedNode mergedChild; using (var tx = s.BeginTransaction()) { - var child = new NumberedNode("child"); + var child = new NumberedNode("child", RoundForDialect(DateTime.Now)); root.AddChild(child); Assert.That(await (s.MergeAsync(root)), Is.SameAs(root)); IEnumerator rit = root.Children.GetEnumerator(); @@ -470,8 +495,16 @@ public async Task MergeTreeAsync() { ClearCounts(); - var root = new Node {Name = "root"}; - var child = new Node {Name = "child"}; + var root = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "root" + }; + var child = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child" + }; using(ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) { @@ -486,7 +519,11 @@ public async Task MergeTreeAsync() root.Description = "The root node"; child.Description = "The child node"; - var secondChild = new Node {Name = "second child"}; + var secondChild = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "second child" + }; root.AddChild(secondChild); @@ -506,8 +543,8 @@ public async Task MergeTreeWithGeneratedIdAsync() { ClearCounts(); - var root = new NumberedNode("root"); - var child = new NumberedNode("child"); + var root = new NumberedNode("root", RoundForDialect(DateTime.Now)); + var child = new NumberedNode("child", RoundForDialect(DateTime.Now)); using(ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) { @@ -522,7 +559,7 @@ public async Task MergeTreeWithGeneratedIdAsync() root.Description = "The root node"; child.Description = "The child node"; - var secondChild = new NumberedNode("second child"); + var secondChild = new NumberedNode("second child", RoundForDialect(DateTime.Now)); root.AddChild(secondChild); @@ -540,7 +577,11 @@ public async Task MergeTreeWithGeneratedIdAsync() [Test] public async Task NoExtraUpdatesOnMergeAsync() { - var node = new Node {Name = "test"}; + var node = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "test" + }; using(ISession s = OpenSession()) using (s.BeginTransaction()) { @@ -673,11 +714,19 @@ public async Task NoExtraUpdatesOnMergeVersionedWithCollectionAsync() [Test] public async Task NoExtraUpdatesOnMergeWithCollectionAsync() { - var parent = new Node {Name = "parent"}; + var parent = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "parent" + }; using(ISession s = OpenSession()) using (s.BeginTransaction()) { - var child = new Node {Name = "child"}; + var child = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child" + }; parent.Children.Add(child); child.Parent = parent; await (s.PersistAsync(parent)); @@ -704,7 +753,12 @@ public async Task NoExtraUpdatesOnMergeWithCollectionAsync() IEnumerator it = parent.Children.GetEnumerator(); it.MoveNext(); it.Current.Description = "child's new description"; - parent.Children.Add(new Node {Name = "second child"}); + parent.Children.Add( + new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "second child" + }); using(var s = OpenSession()) using (s.BeginTransaction()) { diff --git a/src/NHibernate.Test/Async/TypesTest/AbstractDateTimeTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/AbstractDateTimeTypeFixture.cs new file mode 100644 index 00000000000..5cb1c03a231 --- /dev/null +++ b/src/NHibernate.Test/Async/TypesTest/AbstractDateTimeTypeFixture.cs @@ -0,0 +1,439 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Reflection; +using NHibernate.Cfg; +using NHibernate.Driver; +using NHibernate.Engine; +using NHibernate.SqlCommand; +using NHibernate.SqlTypes; +using NHibernate.Tool.hbm2ddl; +using NHibernate.Type; +using NHibernate.Util; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + using System.Threading.Tasks; + using System.Threading; + [TestFixture] + public abstract class AbstractDateTimeTypeFixtureAsync : TypeFixtureBase + { + protected abstract AbstractDateTimeType Type { get; } + protected virtual bool RevisionCheck => true; + + protected const int DateId = 1; + protected const int AdditionalDateId = 2; + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + + var driverClass = ReflectHelper.ClassForName(configuration.GetProperty(Cfg.Environment.ConnectionDriver)); + ClientDriverWithParamsStats.DriverClass = driverClass; + + configuration.SetProperty( + Cfg.Environment.ConnectionDriver, + typeof(ClientDriverWithParamsStats).AssemblyQualifiedName); + } + + protected override void OnSetUp() + { + base.OnSetUp(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeClass + { + Id = DateId, + Value = Now + }; + s.Save(d); + t.Commit(); + } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from DateTimeClass").ExecuteUpdate(); + t.Commit(); + } + } + + protected override void DropSchema() + { + (Sfi.ConnectionProvider.Driver as ClientDriverWithParamsStats)?.CleanUp(); + base.DropSchema(); + } + + [Test] + public async Task NextAsync() + { + var current = DateTime.Parse("2004-01-01"); + var next = await (Type.NextAsync(current, null, CancellationToken.None)); + + Assert.That(next, Is.TypeOf(), "next should be DateTime"); + Assert.That(next, Is.GreaterThan(current), "next should be greater than current"); + } + + [Test] + public async Task SeedAsync() + { + Assert.That(await (Type.SeedAsync(null, CancellationToken.None)), Is.TypeOf(), "seed should be DateTime"); + } + + [Test] + [TestCase(DateTimeKind.Unspecified)] + [TestCase(DateTimeKind.Local)] + [TestCase(DateTimeKind.Utc)] + public async Task ReadWriteAsync(DateTimeKind kind) + { + var entity = new DateTimeClass + { + Id = AdditionalDateId, + Value = GetTestDate(kind) + }; + + var typeKind = GetTypeKind(); + // Now must be acquired before transaction because some db freezes current_timestamp at transaction start, + // like PostgreSQL. https://www.postgresql.org/docs/7.2/static/functions-datetime.html#AEN6700 + // This then wrecks tests with DbTimestampType if the always out of tran Now is called for fetching + // beforeNow only after transaction start. + // And account db accuracy + var beforeNow = Now.AddTicks(-DateAccuracyInTicks); + // Save + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + await (s.SaveAsync(entity)); + if (kind != typeKind && typeKind != DateTimeKind.Unspecified) + { + Assert.That(() => t.CommitAsync(), Throws.TypeOf()); + return; + } + await (t.CommitAsync()); + } + var afterNow = Now.AddTicks(DateAccuracyInTicks); + + if (RevisionCheck) + { + Assert.That(entity.Revision, Is.GreaterThan(beforeNow).And.LessThan(afterNow), "Revision not correctly seeded."); + if (typeKind != DateTimeKind.Unspecified) + Assert.That(entity.Revision.Kind, Is.EqualTo(typeKind), "Revision kind not correctly seeded."); + Assert.That(entity.NullableValue, Is.Null, "NullableValue unexpectedly seeded."); + } + + // Retrieve, compare then update + DateTimeClass retrieved; + using (var s = OpenSession()) + { + using (var t = s.BeginTransaction()) + { + retrieved = await (s.GetAsync(AdditionalDateId)); + + Assert.That(retrieved, Is.Not.Null, "Entity not saved or cannot be retrieved by its key."); + Assert.That(retrieved.Value, Is.EqualTo(GetExpectedValue(entity.Value)), "Unexpected value."); + if (RevisionCheck) + Assert.That(retrieved.Revision, Is.EqualTo(entity.Revision), "Revision should be the same."); + Assert.That(retrieved.NullableValue, Is.EqualTo(entity.NullableValue), "NullableValue should be the same."); + if (typeKind != DateTimeKind.Unspecified) + { + Assert.That(retrieved.Value.Kind, Is.EqualTo(typeKind), "Value kind not correctly retrieved."); + if (RevisionCheck) + Assert.That(retrieved.Revision.Kind, Is.EqualTo(typeKind), "Revision kind not correctly retrieved."); + } + await (t.CommitAsync()); + } + beforeNow = Now.AddTicks(-DateAccuracyInTicks); + using (var t = s.BeginTransaction()) + { + retrieved.NullableValue = GetTestDate(kind); + retrieved.Value = GetTestDate(kind).AddMonths(-1); + await (t.CommitAsync()); + } + afterNow = Now.AddTicks(DateAccuracyInTicks); + } + + if (RevisionCheck) + { + Assert.That( + retrieved.Revision, + Is.GreaterThan(beforeNow).And.LessThan(afterNow).And.GreaterThanOrEqualTo(entity.Revision), + "Revision not correctly incremented."); + if (typeKind != DateTimeKind.Unspecified) + Assert.That(retrieved.Revision.Kind, Is.EqualTo(typeKind), "Revision kind incorrectly changed."); + } + + // Retrieve and compare again + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrievedAgain = await (s.GetAsync(AdditionalDateId)); + + Assert.That(retrievedAgain, Is.Not.Null, "Entity deleted or cannot be retrieved again by its key."); + Assert.That( + retrievedAgain.Value, + Is.EqualTo(GetExpectedValue(retrieved.Value)), + "Unexpected value at second compare."); + if (RevisionCheck) + Assert.That(retrievedAgain.Revision, Is.EqualTo(retrieved.Revision), "Revision should be the same again."); + Assert.That( + retrievedAgain.NullableValue, + Is.EqualTo(GetExpectedValue(retrieved.NullableValue.Value)), + "Unexpected NullableValue at second compare."); + if (typeKind != DateTimeKind.Unspecified) + { + Assert.That(retrievedAgain.Value.Kind, Is.EqualTo(typeKind), "Value kind not correctly retrieved again."); + if (RevisionCheck) + Assert.That(retrievedAgain.Revision.Kind, Is.EqualTo(typeKind), "Revision kind not correctly retrieved again."); + Assert.That( + retrievedAgain.NullableValue.Value.Kind, + Is.EqualTo(typeKind), + "NullableValue kind not correctly retrieved again."); + } + await (t.CommitAsync()); + } + } + + [Test] + public Task DbHasExpectedTypeAsync() + { + try + { + var validator = new SchemaValidator(cfg); + return validator.ValidateAsync(); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + [Test] + public virtual async Task SaveUseExpectedSqlTypeAsync() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeClass + { + Id = 2, + Value = Now, + NullableValue = Now + }; + driver.ClearStats(); + await (s.SaveAsync(d)); + await (t.CommitAsync()); + } + + // 2 properties + revision + AssertSqlType(driver, 3, true); + } + + [Test] + public virtual async Task UpdateUseExpectedSqlTypeAsync() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = await (s.GetAsync(DateId)); + d.Value = Now; + d.NullableValue = Now; + driver.ClearStats(); + await (t.CommitAsync()); + } + + // 2 properties + revision x 2 (check + update) + AssertSqlType(driver, 4, true); + } + + [Test] + public virtual async Task QueryUseExpectedSqlTypeAsync() + { + if (!TestDialect.SupportsNonDataBoundCondition) + Assert.Ignore("Dialect does not support the test query"); + + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetDateTime("value", Now) + .SetDateTime("nullableValue", Now) + .SetDateTime("revision", Now) + .SetDateTime("other1", Now) + .SetDateTime("other2", Now); + driver.ClearStats(); + await (q.ListAsync()); + await (t.CommitAsync()); + } + + AssertSqlType(driver, 5, false); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetParameter("value", Now, Type) + .SetParameter("nullableValue", Now, Type) + .SetParameter("revision", Now, Type) + .SetParameter("other1", Now, Type) + .SetParameter("other2", Now, Type); + driver.ClearStats(); + await (q.ListAsync()); + await (t.CommitAsync()); + } + + AssertSqlType(driver, 5, true); + } + + private void AssertSqlType(ClientDriverWithParamsStats driver, int expectedCount, bool exactType) + { + var typeSqlTypes = Type.SqlTypes(Sfi); + if (typeSqlTypes.Any(t => t is DateTime2SqlType)) + { + var expectedType = exactType ? typeSqlTypes.First(t => t is DateTime2SqlType) : SqlTypeFactory.DateTime2; + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.DateTime2 usage count."); + Assert.That(driver.GetCount(DbType.DateTime), Is.EqualTo(0), "Found unexpected DbType.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected DbType.DateTime2 usage count."); + } + else if (typeSqlTypes.Any(t => t is DateTimeSqlType)) + { + var expectedType = exactType ? typeSqlTypes.First(t => t is DateTimeSqlType) : SqlTypeFactory.DateTime; + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime2), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime2 usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.DateTime usage count."); + Assert.That(driver.GetCount(DbType.DateTime2), Is.EqualTo(0), "Found unexpected DbType.DateTime2 usages."); + Assert.That(driver.GetCount(expectedType), Is.EqualTo(expectedCount), "Unexpected DbType.DateTime usage count."); + } + else if (typeSqlTypes.Any(t => Equals(t, SqlTypeFactory.Date))) + { + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime usages."); + Assert.That( + driver.GetCount(SqlTypeFactory.Date), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.Date usage count."); + Assert.That(driver.GetCount(DbType.DateTime), Is.EqualTo(0), "Found unexpected DbType.DateTime usages."); + Assert.That(driver.GetCount(DbType.Date), Is.EqualTo(expectedCount), "Unexpected DbType.Date usage count."); + } + else + { + Assert.Ignore("Test does not involve tested types"); + } + } + + protected virtual long DateAccuracyInTicks => Dialect.TimestampResolutionInTicks; + + protected virtual DateTime Now => GetTypeKind() == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.Now; + + protected virtual DateTime GetTestDate(DateTimeKind kind) + { + return AbstractDateTimeType.Round( + kind == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.SpecifyKind(DateTime.Now, kind), + DateAccuracyInTicks) + // Take another date than now for checking the value do not get overridden by seeding. + .AddDays(1); + } + + private DateTime GetExpectedValue(DateTime value) + { + var expectedValue = value; + var typeKind = GetTypeKind(); + if (typeKind != DateTimeKind.Unspecified && typeKind != value.Kind && value.Kind != DateTimeKind.Unspecified) + { + expectedValue = typeKind == DateTimeKind.Local ? expectedValue.ToLocalTime() : expectedValue.ToUniversalTime(); + } + return expectedValue; + } + + /// + /// Return a date time still considered equal but as different as possible. + /// + /// The originale date time. + /// An equal date time. + protected virtual DateTime GetSameDate(DateTime original) + { + if (GetTypeKind() != DateTimeKind.Unspecified) + return new DateTime(original.Ticks, original.Kind); + + switch (original.Kind) + { + case DateTimeKind.Local: + return DateTime.SpecifyKind(original, DateTimeKind.Unspecified); + case DateTimeKind.Unspecified: + return DateTime.SpecifyKind(original, DateTimeKind.Utc); + default: + return DateTime.SpecifyKind(original, DateTimeKind.Local); + } + } + + /// + /// Return a different date time but as few different as possible. + /// + /// The originale date time. + /// An inequal date time. + protected virtual DateTime GetDifferentDate(DateTime original) + { + return original.AddTicks(DateAccuracyInTicks); + } + + private static readonly PropertyInfo _kindProperty = + typeof(AbstractDateTimeType).GetProperty("Kind", BindingFlags.Instance | BindingFlags.NonPublic); + + protected DateTimeKind GetTypeKind() + { + return (DateTimeKind) _kindProperty.GetValue(Type); + } + } +} diff --git a/src/NHibernate.Test/Async/TypesTest/DateTime2TypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/DateTime2TypeFixture.cs index 6b9554fb3e4..e30eaa872aa 100644 --- a/src/NHibernate.Test/Async/TypesTest/DateTime2TypeFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/DateTime2TypeFixture.cs @@ -9,34 +9,44 @@ using System; +using NHibernate.Driver; +using NHibernate.SqlTypes; using NHibernate.Type; using NUnit.Framework; namespace NHibernate.Test.TypesTest { using System.Threading.Tasks; - using System.Threading; /// /// TestFixtures for the . /// [TestFixture] - public class DateTime2TypeFixtureAsync + [Obsolete] + public class DateTime2TypeFixtureAsync : AbstractDateTimeTypeFixtureAsync { - [Test] - public async Task NextAsync() - { - DateTimeType type = NHibernateUtil.DateTime2; - object current = DateTime.Now.AddMilliseconds(-1); - object next = await (type.NextAsync(current, null, CancellationToken.None)); - - Assert.That(next, Is.TypeOf().And.GreaterThan(current)); - } - - [Test] - public async Task SeedAsync() - { - DateTimeType type = NHibernateUtil.DateTime; - Assert.IsTrue(await (type.SeedAsync(null, CancellationToken.None)) is DateTime, "seed should be DateTime"); - } + protected override bool AppliesTo(Dialect.Dialect dialect) => + TestDialect.SupportsSqlType(SqlTypeFactory.DateTime2); + + protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) => + // Cannot handle DbType.DateTime2 via .Net ODBC. + !(factory.ConnectionProvider.Driver is OdbcDriver); + + protected override string TypeName => "DateTime2"; + protected override AbstractDateTimeType Type => NHibernateUtil.DateTime2; + } + + [TestFixture] + [Obsolete] + public class DateTime2TypeWithScaleFixtureAsync : DateTimeTypeWithScaleFixtureAsync + { + protected override bool AppliesTo(Dialect.Dialect dialect) => + TestDialect.SupportsSqlType(SqlTypeFactory.DateTime2); + + protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) => + // Cannot handle DbType.DateTime2 via .Net ODBC. + !(factory.ConnectionProvider.Driver is OdbcDriver); + + protected override string TypeName => "DateTime2WithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetDateTime2Type(3); } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/TypesTest/DateTimeOffsetTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/DateTimeOffsetTypeFixture.cs new file mode 100644 index 00000000000..a6ef854fad2 --- /dev/null +++ b/src/NHibernate.Test/Async/TypesTest/DateTimeOffsetTypeFixture.cs @@ -0,0 +1,394 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using System.Data; +using System.Linq; +using NHibernate.Cfg; +using NHibernate.Driver; +using NHibernate.SqlTypes; +using NHibernate.Tool.hbm2ddl; +using NHibernate.Type; +using NHibernate.Util; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + using System.Threading.Tasks; + using System.Threading; + [TestFixture] + public class DateTimeOffsetTypeFixtureAsync : TypeFixtureBase + { + protected override string TypeName => "DateTimeOffset"; + protected virtual DateTimeOffsetType Type => NHibernateUtil.DateTimeOffset; + protected virtual bool RevisionCheck => true; + + protected const int DateId = 1; + protected const int AdditionalDateId = 2; + + protected override bool AppliesTo(Dialect.Dialect dialect) => + TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet); + + protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) => + // Cannot handle DbType.DateTimeOffset via .Net ODBC. + !(factory.ConnectionProvider.Driver is OdbcDriver); + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + + var driverClass = ReflectHelper.ClassForName(configuration.GetProperty(Cfg.Environment.ConnectionDriver)); + ClientDriverWithParamsStats.DriverClass = driverClass; + + configuration.SetProperty( + Cfg.Environment.ConnectionDriver, + typeof(ClientDriverWithParamsStats).AssemblyQualifiedName); + } + + protected override void OnSetUp() + { + base.OnSetUp(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeOffsetClass + { + Id = DateId, + Value = Now + }; + s.Save(d); + t.Commit(); + } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from DateTimeOffsetClass").ExecuteUpdate(); + t.Commit(); + } + } + + protected override void DropSchema() + { + (Sfi.ConnectionProvider.Driver as ClientDriverWithParamsStats)?.CleanUp(); + base.DropSchema(); + } + + [Test] + public async Task NextAsync() + { + var current = DateTimeOffset.Parse("2004-01-01"); + var next = await (Type.NextAsync(current, null, CancellationToken.None)); + + Assert.That(next, Is.TypeOf(), "next should be DateTimeOffset"); + Assert.That(next, Is.GreaterThan(current), "next should be greater than current"); + } + + [Test] + public async Task SeedAsync() + { + Assert.That(await (Type.SeedAsync(null, CancellationToken.None)), Is.TypeOf(), "seed should be DateTime"); + } + + [Test] + public async Task ReadWriteAsync() + { + var entity = new DateTimeOffsetClass + { + Id = AdditionalDateId, + Value = GetTestDate() + }; + + // Now must be acquired before transaction because some db freezes current_timestamp at transaction start, + // like PostgreSQL. https://www.postgresql.org/docs/7.2/static/functions-datetime.html#AEN6700 + // This then wrecks tests with DbTimestampType if the always out of tran Now is called for fetching + // beforeNow only after transaction start. + // And account db accuracy + var beforeNow = Now.AddTicks(-DateAccuracyInTicks); + // Save + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + await (s.SaveAsync(entity)); + await (t.CommitAsync()); + } + var afterNow = Now.AddTicks(DateAccuracyInTicks); + + if (RevisionCheck) + { + Assert.That(entity.Revision, Is.GreaterThan(beforeNow).And.LessThan(afterNow), "Revision not correctly seeded."); + Assert.That(entity.NullableValue, Is.Null, "NullableValue unexpectedly seeded."); + } + + // Retrieve, compare then update + DateTimeOffsetClass retrieved; + using (var s = OpenSession()) + { + using (var t = s.BeginTransaction()) + { + retrieved = await (s.GetAsync(AdditionalDateId)); + + Assert.That(retrieved, Is.Not.Null, "Entity not saved or cannot be retrieved by its key."); + Assert.That(retrieved.Value, Is.EqualTo(entity.Value), "Unexpected value."); + if (RevisionCheck) + Assert.That(retrieved.Revision, Is.EqualTo(entity.Revision), "Revision should be the same."); + Assert.That(retrieved.NullableValue, Is.EqualTo(entity.NullableValue), "NullableValue should be the same."); + await (t.CommitAsync()); + } + beforeNow = Now.AddTicks(-DateAccuracyInTicks); + using (var t = s.BeginTransaction()) + { + retrieved.NullableValue = GetTestDate(); + retrieved.Value = GetTestDate().AddMonths(-1); + await (t.CommitAsync()); + } + afterNow = Now.AddTicks(DateAccuracyInTicks); + } + + if (RevisionCheck) + { + Assert.That( + retrieved.Revision, + Is.GreaterThan(beforeNow).And.LessThan(afterNow).And.GreaterThanOrEqualTo(entity.Revision), + "Revision not correctly incremented."); + } + + // Retrieve and compare again + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrievedAgain = await (s.GetAsync(AdditionalDateId)); + + Assert.That(retrievedAgain, Is.Not.Null, "Entity deleted or cannot be retrieved again by its key."); + Assert.That( + retrievedAgain.Value, + Is.EqualTo(retrieved.Value), + "Unexpected value at second compare."); + if (RevisionCheck) + Assert.That(retrievedAgain.Revision, Is.EqualTo(retrieved.Revision), "Revision should be the same again."); + Assert.That( + retrievedAgain.NullableValue, + Is.EqualTo(retrieved.NullableValue.Value), + "Unexpected NullableValue at second compare."); + await (t.CommitAsync()); + } + } + + [Test] + public Task DbHasExpectedTypeAsync() + { + try + { + var validator = new SchemaValidator(cfg); + return validator.ValidateAsync(); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + [Test] + public virtual async Task SaveUseExpectedSqlTypeAsync() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeOffsetClass + { + Id = 2, + Value = Now, + NullableValue = Now + }; + driver.ClearStats(); + await (s.SaveAsync(d)); + await (t.CommitAsync()); + } + + // 2 properties + revision + AssertSqlType(driver, 3, true); + } + + [Test] + public virtual async Task UpdateUseExpectedSqlTypeAsync() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = await (s.GetAsync(DateId)); + d.Value = Now; + d.NullableValue = Now; + driver.ClearStats(); + await (t.CommitAsync()); + } + + // 2 properties + revision x 2 (check + update) + AssertSqlType(driver, 4, true); + } + + [Test] + public virtual async Task QueryUseExpectedSqlTypeAsync() + { + if (!TestDialect.SupportsNonDataBoundCondition) + Assert.Ignore("Dialect does not support the test query"); + + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeOffsetClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetDateTimeOffset("value", Now) + .SetDateTimeOffset("nullableValue", Now) + .SetDateTimeOffset("revision", Now) + .SetDateTimeOffset("other1", Now) + .SetDateTimeOffset("other2", Now); + driver.ClearStats(); + await (q.ListAsync()); + await (t.CommitAsync()); + } + + AssertSqlType(driver, 5, false); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeOffsetClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetParameter("value", Now, Type) + .SetParameter("nullableValue", Now, Type) + .SetParameter("revision", Now, Type) + .SetParameter("other1", Now, Type) + .SetParameter("other2", Now, Type); + driver.ClearStats(); + await (q.ListAsync()); + await (t.CommitAsync()); + } + + AssertSqlType(driver, 5, true); + } + + private void AssertSqlType(ClientDriverWithParamsStats driver, int expectedCount, bool exactType) + { + var typeSqlTypes = Type.SqlTypes(Sfi); + if (typeSqlTypes.Any(t => t is DateTimeOffsetSqlType)) + { + var expectedType = exactType ? typeSqlTypes.First(t => t is DateTimeOffsetSqlType) : SqlTypeFactory.DateTimeOffSet; + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.DateTime2 usage count."); + Assert.That(driver.GetCount(DbType.DateTime), Is.EqualTo(0), "Found unexpected DbType.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected DbType.DateTime2 usage count."); + } + else + { + Assert.Ignore("Test does not involve tested types"); + } + } + + protected virtual long DateAccuracyInTicks => Dialect.TimestampResolutionInTicks; + + protected virtual DateTimeOffset Now => DateTimeOffset.Now; + + protected virtual DateTimeOffset GetTestDate() + { + return DateTimeOffsetType.Round(Now, DateAccuracyInTicks) + // Take another date than now for checking the value do not get overridden by seeding. + .AddDays(1); + } + + /// + /// Return a date time still considered equal but as different as possible. + /// + /// The originale date time. + /// An equal date time. + protected virtual DateTimeOffset GetSameDate(DateTimeOffset original) + { + return new DateTimeOffset(original.Ticks, original.Offset); + } + + /// + /// Return a different date time but as few different as possible. + /// + /// The originale date time. + /// An inequal date time. + protected virtual DateTimeOffset GetDifferentDate(DateTimeOffset original) + { + return original.AddTicks(DateAccuracyInTicks); + } + } + + [TestFixture] + public class DateTimeOffsetTypeWithScaleFixtureAsync : DateTimeOffsetTypeFixtureAsync + { + protected override string TypeName => "DateTimeOffsetWithScale"; + protected override DateTimeOffsetType Type => (DateTimeOffsetType)TypeFactory.GetDateTimeOffsetType(3); + protected override long DateAccuracyInTicks => Math.Max(TimeSpan.TicksPerMillisecond, base.DateAccuracyInTicks); + // The timestamp rounding in seeding does not account scale. + protected override bool RevisionCheck => false; + + [Test] + public async Task LowerDigitsAreIgnoredAsync() + { + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseDate = new DateTimeOffset(2017, 10, 01, 17, 55, 24, 548, TimeSpan.FromHours(2)); + var entity = new DateTimeOffsetClass + { + Id = AdditionalDateId, + Value = baseDate.AddTicks(TimeSpan.TicksPerMillisecond / 3) + }; + Assert.That(entity.Value, Is.Not.EqualTo(baseDate)); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + await (s.SaveAsync(entity)); + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = await (s.LoadAsync(AdditionalDateId)); + Assert.That(retrieved.Value, Is.EqualTo(baseDate)); + await (t.CommitAsync()); + } + } + } +} diff --git a/src/NHibernate.Test/Async/TypesTest/DateTimeTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/DateTimeTypeFixture.cs index da73b061628..086da356ed5 100644 --- a/src/NHibernate.Test/Async/TypesTest/DateTimeTypeFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/DateTimeTypeFixture.cs @@ -9,36 +9,138 @@ using System; +using NHibernate.Cfg; +using NHibernate.Dialect; using NHibernate.Type; using NUnit.Framework; +using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Test.TypesTest { using System.Threading.Tasks; - using System.Threading; /// /// TestFixtures for the . /// [TestFixture] - public class DateTimeTypeFixtureAsync + public class DateTimeTypeFixtureAsync : AbstractDateTimeTypeFixtureAsync { + protected override string TypeName => "DateTime"; + protected override AbstractDateTimeType Type => NHibernateUtil.DateTime; + } + + /// + /// TestFixtures for the . + /// + [TestFixture] + public class DateTimeTypeWithScaleFixtureAsync : AbstractDateTimeTypeFixtureAsync + { + protected override string TypeName => "DateTimeWithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetDateTimeType(3); + protected override long DateAccuracyInTicks => Math.Max(TimeSpan.TicksPerMillisecond, base.DateAccuracyInTicks); + // The timestamp rounding in seeding does not account scale. + protected override bool RevisionCheck => false; + [Test] - public async Task NextAsync() + public async Task LowerDigitsAreIgnoredAsync() { - DateTimeType type = (DateTimeType) NHibernateUtil.DateTime; - object current = DateTime.Parse("2004-01-01"); - object next = await (type.NextAsync(current, null, CancellationToken.None)); + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseDate = new DateTime(2017, 10, 01, 17, 55, 24, 548, GetTypeKind()); + var entity = new DateTimeClass + { + Id = AdditionalDateId, + Value = baseDate.AddTicks(TimeSpan.TicksPerMillisecond / 3) + }; + Assert.That(entity.Value, Is.Not.EqualTo(baseDate)); - Assert.IsTrue(next is DateTime, "Next should be DateTime"); - Assert.IsTrue((DateTime) next > (DateTime) current, - "next should be greater than current (could be equal depending on how quickly this occurs)"); + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + await (s.SaveAsync(entity)); + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = await (s.LoadAsync(AdditionalDateId)); + Assert.That(retrieved.Value, Is.EqualTo(baseDate)); + await (t.CommitAsync()); + } } + } - [Test] - public async Task SeedAsync() + // Testing SQL Server 2008 with datetime in db instead of datetime2 + [TestFixture] + public class DateTimeTypeMixedDateTimeFixtureAsync : DateTimeTypeFixtureAsync + { + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return base.AppliesTo(dialect) && dialect is MsSql2008Dialect; + } + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + + configuration.SetProperty(Environment.SqlTypesKeepDateTime, "true"); + } + + /* Uncomment for testing how it fails if NHibernate uses DateTime2 while the db column is DateTime. + * It fails two thirds of times on update with an undue optimistic locking exception, due to the datetimes + * ending with 3.333ms (transmitted as 3) or 6.666ms (transmitted as 7), which does not match the datetime2 + * sent by NHibernate for checking the revision has not changed. Demonstrated by UpdateUseExpectedSqlType. + * It furthermore causes undue optimistic failures if re-updating the same entity without loading it in-between, + * as demonstrated by ReadWrite. + * Another way to demonstrate this is to use a + * mapping, without using SqlTypesKeepDateTime. This causes the column to be typed datetime in db but the + * parameters are still transmitted as datetime2 because this sql-type attribute is only used in DDL. + * + protected override DebugSessionFactory BuildSessionFactory() + { + cfg.SetProperty(Environment.SqlTypesKeepDateTime, "false"); + try + { + return base.BuildSessionFactory(); + } + finally + { + cfg.SetProperty(Environment.SqlTypesKeepDateTime, "true"); + } + } + //*/ + } + + /// + /// TestFixtures for the . + /// + [TestFixture] + public class DateTimeNoMsTypeFixtureAsync : AbstractDateTimeTypeFixtureAsync + { + protected override string TypeName => "DateTimeNoMs"; + protected override AbstractDateTimeType Type => NHibernateUtil.DateTimeNoMs; + protected override bool RevisionCheck => false; + protected override long DateAccuracyInTicks => TimeSpan.TicksPerSecond; + + protected override DateTime GetTestDate(DateTimeKind kind) + { + var date = base.GetTestDate(kind); + return new DateTime( + date.Year, + date.Month, + date.Day, + date.Hour, + date.Minute, + date.Second, + 0, + kind); + } + + protected override DateTime GetSameDate(DateTime original) { - DateTimeType type = (DateTimeType) NHibernateUtil.DateTime; - Assert.IsTrue(await (type.SeedAsync(null, CancellationToken.None)) is DateTime, "seed should be DateTime"); + var date = base.GetSameDate(original); + return date.AddMilliseconds(date.Millisecond < 500 ? 500 : -500); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/TypesTest/DateTypeTest.cs b/src/NHibernate.Test/Async/TypesTest/DateTypeTest.cs index 72ceadfc151..3dd89c51e01 100644 --- a/src/NHibernate.Test/Async/TypesTest/DateTypeTest.cs +++ b/src/NHibernate.Test/Async/TypesTest/DateTypeTest.cs @@ -20,11 +20,20 @@ namespace NHibernate.Test.TypesTest using System.Threading; [TestFixture] - public class DateTypeFixtureAsync : TypeFixtureBase + public class DateTypeFixtureAsync : AbstractDateTimeTypeFixtureAsync { - protected override string TypeName + protected override string TypeName => "Date"; + protected override AbstractDateTimeType Type => NHibernateUtil.Date; + protected override bool RevisionCheck => false; + protected override long DateAccuracyInTicks => TimeSpan.TicksPerDay; + + protected override DateTime GetTestDate(DateTimeKind kind) + => base.GetTestDate(kind).Date; + + protected override DateTime GetSameDate(DateTime original) { - get { return "Date"; } + var date = base.GetSameDate(original); + return date.AddHours(date.Hour < 12 ? 12 : -12); } [Test] @@ -78,20 +87,39 @@ public Task ReadWriteYear750Async() private async Task ReadWriteAsync(DateTime expected, CancellationToken cancellationToken = default(CancellationToken)) { // Add an hour to check it is correctly ignored once read back from db. - var basic = new DateClass { DateValue = expected.AddHours(1) }; - object savedId; + var basic = new DateTimeClass { Id = AdditionalDateId, Value = expected.AddHours(1) }; using (var s = OpenSession()) { - savedId = await (s.SaveAsync(basic, cancellationToken)); + await (s.SaveAsync(basic, cancellationToken)); await (s.FlushAsync(cancellationToken)); } using (var s = OpenSession()) { - basic = await (s.GetAsync(savedId, cancellationToken)); - Assert.That(basic.DateValue, Is.EqualTo(expected)); + basic = await (s.GetAsync(AdditionalDateId, cancellationToken)); + Assert.That(basic.Value, Is.EqualTo(expected)); await (s.DeleteAsync(basic, cancellationToken)); await (s.FlushAsync(cancellationToken)); } } + + // They furthermore cause some issues with Sql Server Ce, which switch DbType.Date for DbType.DateTime + // on its parameters. + [Ignore("Test relevant for datetime, not for date.")] + public override Task SaveUseExpectedSqlTypeAsync() + { + return Task.CompletedTask; + } + + [Ignore("Test relevant for datetime, not for date.")] + public override Task UpdateUseExpectedSqlTypeAsync() + { + return Task.CompletedTask; + } + + [Ignore("Test relevant for datetime, not for date.")] + public override Task QueryUseExpectedSqlTypeAsync() + { + return Task.CompletedTask; + } } } diff --git a/src/NHibernate.Test/Async/TypesTest/DbTimestampTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/DbTimestampTypeFixture.cs new file mode 100644 index 00000000000..69bd37efd86 --- /dev/null +++ b/src/NHibernate.Test/Async/TypesTest/DbTimestampTypeFixture.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using NHibernate.Type; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + using System.Threading.Tasks; + [TestFixture] + public class DbTimestampTypeFixtureAsync : AbstractDateTimeTypeFixtureAsync + { + protected override string TypeName => "DbTimestamp"; + protected override AbstractDateTimeType Type => NHibernateUtil.DbTimestamp; + protected override DateTime Now => (DateTime)Type.Seed(_session?.GetSessionImplementation()); + private ISession _session; + + protected override void OnSetUp() + { + _session = OpenSession(); + base.OnSetUp(); + } + + protected override void OnTearDown() + { + base.OnTearDown(); + _session.Dispose(); + _session = null; + } + } +} diff --git a/src/NHibernate.Test/Async/TypesTest/LocalDateTimeTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/LocalDateTimeTypeFixture.cs index 9fa9e866760..76648b69643 100644 --- a/src/NHibernate.Test/Async/TypesTest/LocalDateTimeTypeFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/LocalDateTimeTypeFixture.cs @@ -8,47 +8,33 @@ //------------------------------------------------------------------------------ -using System; +using NHibernate.Type; using NUnit.Framework; namespace NHibernate.Test.TypesTest { using System.Threading.Tasks; /// - /// The Unit Tests for the UtcDateTimeType. + /// The Unit Tests for the LocalDateTimeType. /// [TestFixture] - public class LocalDateTimeTypeFixtureAsync : TypeFixtureBase + public class LocalDateTimeTypeFixtureAsync : AbstractDateTimeTypeFixtureAsync { - protected override string TypeName - { - get { return "DateTime"; } - } - - [Test] - public async Task ReadWriteAsync() - { - DateTime val = DateTime.UtcNow; - DateTime expected = new DateTime(val.Year, val.Month, val.Day, val.Hour, val.Minute, val.Second, DateTimeKind.Local); - - DateTimeClass basic = new DateTimeClass(); - basic.Id = 1; - basic.LocalDateTimeValue = val; - - ISession s = OpenSession(); - await (s.SaveAsync(basic)); - await (s.FlushAsync()); - s.Close(); - - s = OpenSession(); - basic = (DateTimeClass) await (s.LoadAsync(typeof (DateTimeClass), 1)); + protected override string TypeName => "LocalDateTime"; + protected override AbstractDateTimeType Type => NHibernateUtil.LocalDateTime; + } - Assert.AreEqual(DateTimeKind.Local, basic.LocalDateTimeValue.Value.Kind); - Assert.AreEqual(expected, basic.LocalDateTimeValue.Value); + [TestFixture] + public class LocalDateTimeTypeWithScaleFixtureAsync : DateTimeTypeWithScaleFixtureAsync + { + protected override string TypeName => "LocalDateTimeWithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetLocalDateTimeType(3); + } - await (s.DeleteAsync(basic)); - await (s.FlushAsync()); - s.Close(); - } + [TestFixture] + public class LocalDateTimeNoMsTypeFixtureAsync : DateTimeNoMsTypeFixtureAsync + { + protected override string TypeName => "LocalDateTimeNoMs"; + protected override AbstractDateTimeType Type => NHibernateUtil.LocalDateTimeNoMs; } } diff --git a/src/NHibernate.Test/Async/TypesTest/StringTypeWithLengthFixture.cs b/src/NHibernate.Test/Async/TypesTest/StringTypeWithLengthFixture.cs index 5d21e30110e..be75a5c5805 100644 --- a/src/NHibernate.Test/Async/TypesTest/StringTypeWithLengthFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/StringTypeWithLengthFixture.cs @@ -15,9 +15,9 @@ using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Exceptions; -using NHibernate.Linq; using NHibernate.Mapping.ByCode; using NUnit.Framework; +using NHibernate.Linq; namespace NHibernate.Test.TypesTest { @@ -62,6 +62,16 @@ protected override HbmMapping GetMappings() return mapper.CompileMappingForAllExplicitlyAddedEntities(); } + protected override void OnTearDown() + { + base.OnTearDown(); + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from System.Object").ExecuteUpdate(); + t.Commit(); + } + } [Test] [Description("Values longer than the maximum possible string length " + @@ -100,6 +110,57 @@ public Task ShouldPreventInsertionOfVeryLongStringThatWouldBeTruncatedAsync() } } + // NH-4083 + [Test] + public async Task CanCompareShortValueWithLongStringAsync() + { + var maxStringLength = GetLongStringMappedLength(); + var longString = new string('x', maxStringLength); + using (var s = OpenSession()) + { + var b = new StringClass { LongStringValue = longString }; + await (s.SaveAsync(b)); + await (s.FlushAsync()); + } + + using (var s = OpenSession()) + { + var q = s.CreateQuery("from StringClass s where s.LongStringValue != :shortString") + // Do not replace with SetString, otherwise length will be unspecified. + .SetParameter("shortString", "aaa"); + var sc = await (q.UniqueResultAsync()); + Assert.That(sc, Is.Not.Null); + Assert.That(sc.LongStringValue, Is.EqualTo(longString)); + } + } + + [Test] + public async Task CanCompareLongValueWithLongStringAsync() + { + var maxStringLength = GetLongStringMappedLength(); + + if (Sfi.ConnectionProvider.Driver is OdbcDriver && maxStringLength >= 2000) + Assert.Ignore("Odbc wrecks nvarchar parameter types when they are longer than 2000, it switch them to ntext"); + + var longString = new string('x', maxStringLength); + using (var s = OpenSession()) + { + var b = new StringClass { LongStringValue = longString }; + await (s.SaveAsync(b)); + await (s.FlushAsync()); + } + + using (var s = OpenSession()) + { + var q = s.CreateQuery("from StringClass s where s.LongStringValue = :longString") + // Do not replace with SetString, otherwise length will be unspecified. + .SetParameter("longString", longString); + var sc = await (q.UniqueResultAsync()); + Assert.That(sc, Is.Not.Null); + Assert.That(sc.LongStringValue, Is.EqualTo(longString)); + } + } + [Test] [Description("Values longer than the mapped string length " + "should raise an exception if they would otherwise be truncated.")] @@ -133,7 +194,6 @@ public Task ShouldPreventInsertionOfTooLongStringThatWouldBeTruncatedAsync() } } - private async Task AssertFailedInsertExceptionDetailsAndEmptyTableAsync(Exception ex, CancellationToken cancellationToken = default(CancellationToken)) { // We can get different sort of exceptions. @@ -171,43 +231,92 @@ public Task ShouldPreventInsertionOfTooLongStringThatWouldBeTruncatedAsync() } } - - /// - /// Some test cases doesn't work during some scenarios for well-known reasons. If the test - /// fails under these circumstances, mark it as IGNORED. If it _stops_ failing, mark it - /// as a FAILURE so that it can be investigated. - /// - private void AssertExpectedFailureOrNoException(Exception exception, bool requireExceptionAndIgnoreTest) + [Test] + public async Task CriteriaLikeParameterCanExceedColumnSizeAsync() { - if (requireExceptionAndIgnoreTest) + using (ISession s = OpenSession()) + using (s.BeginTransaction()) { - Assert.NotNull( - exception, - "Test was expected to have a well-known, but ignored, failure for the current configuration. If " + - "that expected failure no longer occurs, it may now be possible to remove this exception."); + await (s.SaveAsync(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"})); + await (s.SaveAsync(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"})); - Assert.Ignore("This test is known to fail for the current configuration."); - } + var aaItems = + await (s.CreateCriteria() + .Add(Restrictions.Like("StringValue", "%AAAAAAAAA%")) + .ListAsync()); - // If the above didn't ignore the exception, it's for real - rethrow to trigger test failure. - if (exception != null) - throw new Exception("Wrapped exception.", exception); + Assert.That(aaItems.Count, Is.EqualTo(2)); + } } + [Test] + public async Task HqlLikeParameterCanExceedColumnSizeAsync() + { + using (ISession s = OpenSession()) + using (s.BeginTransaction()) + { + await (s.SaveAsync(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"})); + await (s.SaveAsync(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"})); + + var aaItems = + await (s.CreateQuery("from StringClass s where s.StringValue like :likeValue") + .SetParameter("likeValue", "%AAAAAAAAA%") + .ListAsync()); + + Assert.That(aaItems.Count, Is.EqualTo(2)); + } + } - private TException CatchException(System.Action action) - where TException : Exception + [Test] + public async Task CriteriaEqualityParameterCanExceedColumnSizeAsync() { - try + if (!TestDialect.SupportsNonDataBoundCondition) + { + // Doesn't work on Firebird due to Firebird not figuring out parameter types on its own. + Assert.Ignore("Dialect does not support this test"); + } + + // We should be able to query a column with a value longer than + // the specified column size, to avoid tedious exceptions. + using (ISession s = OpenSession()) + using (s.BeginTransaction()) { - action(); + await (s.SaveAsync(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"})); + await (s.SaveAsync(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"})); + + var aaItems = + await (s.CreateCriteria() + .Add(Restrictions.Eq("StringValue", "AAAAAAAAABx")) + .ListAsync()); + + Assert.That(aaItems.Count, Is.EqualTo(0)); } - catch (TException exception) + } + + [Test] + public async Task HqlEqualityParameterCanExceedColumnSizeAsync() + { + if (!TestDialect.SupportsNonDataBoundCondition) { - return exception; + // Doesn't work on Firebird due to Firebird not figuring out parameter types on its own. + Assert.Ignore("Dialect does not support this test"); } - return null; + // We should be able to query a column with a value longer than + // the specified column size, to avoid tedious exceptions. + using (ISession s = OpenSession()) + using (s.BeginTransaction()) + { + await (s.SaveAsync(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"})); + await (s.SaveAsync(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"})); + + var aaItems = + await (s.CreateQuery("from StringClass s where s.StringValue = :likeValue") + .SetParameter("likeValue", "AAAAAAAAABx") + .ListAsync()); + + Assert.That(aaItems.Count, Is.EqualTo(0)); + } } } } diff --git a/src/NHibernate.Test/Async/TypesTest/TimeAsTimeSpanTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/TimeAsTimeSpanTypeFixture.cs index e3cf6f68e01..134fdee1b32 100644 --- a/src/NHibernate.Test/Async/TypesTest/TimeAsTimeSpanTypeFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/TimeAsTimeSpanTypeFixture.cs @@ -50,6 +50,18 @@ protected override string TypeName get { return "TimeAsTimeSpan"; } } + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from TimeAsTimeSpanClass").ExecuteUpdate(); + tx.Commit(); + } + } + [Test] public async Task SavingAndRetrievingAsync() { @@ -79,13 +91,37 @@ public async Task SavingAndRetrievingAsync() Assert.AreEqual(entityReturned.TimeSpanValue.Minutes, ticks.Minutes); Assert.AreEqual(entityReturned.TimeSpanValue.Seconds, ticks.Seconds); } + } - using (ISession s = OpenSession()) - using (ITransaction tx = s.BeginTransaction()) + [Test] + public async Task LowerDigitsAreIgnoredAsync() + { + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseTime = new TimeSpan(0, 17, 55, 24, 548); + var entity = new TimeAsTimeSpanClass { - await (s.DeleteAsync(entityReturned)); - await (tx.CommitAsync()); + TimeSpanWithScale = baseTime.Add(TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond / 3)) + }; + Assert.That(entity.TimeSpanWithScale, Is.Not.EqualTo(baseTime)); + + int id; + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + await (s.SaveAsync(entity)); + id = entity.Id; + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = await (s.LoadAsync(id)); + Assert.That(retrieved.TimeSpanWithScale, Is.EqualTo(baseTime)); + await (t.CommitAsync()); } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/TypesTest/TimeFixture.cs b/src/NHibernate.Test/Async/TypesTest/TimeFixture.cs new file mode 100644 index 00000000000..9077cb86012 --- /dev/null +++ b/src/NHibernate.Test/Async/TypesTest/TimeFixture.cs @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using NHibernate.Type; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + using System.Threading.Tasks; + [TestFixture] + public class TimeFixtureAsync : TypeFixtureBase + { + protected override string TypeName => "Time"; + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from TimeClass").ExecuteUpdate(); + tx.Commit(); + } + } + + [Test] + public async Task SavingAndRetrievingAsync() + { + var ticks = DateTime.Parse("23:59:59"); + + var entity = + new TimeClass + { + TimeValue = ticks + }; + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + await (s.SaveAsync(entity)); + await (tx.CommitAsync()); + } + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + var entityReturned = await (s.CreateQuery("from TimeClass").UniqueResultAsync()); + + Assert.That(entityReturned.TimeValue.TimeOfDay, Is.EqualTo(ticks.TimeOfDay)); + Assert.That(ticks.Hour, Is.EqualTo(entityReturned.TimeValue.Hour)); + Assert.That(ticks.Minute, Is.EqualTo(entityReturned.TimeValue.Minute)); + Assert.That(ticks.Second, Is.EqualTo(entityReturned.TimeValue.Second)); + await (tx.CommitAsync()); + } + } + + [Test] + public async Task LowerDigitsAreIgnoredAsync() + { + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseTime = new DateTime(1990, 01, 01, 17, 55, 24, 548); + var entity = new TimeClass + { + TimeWithScale = baseTime.Add(TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond / 3)) + }; + Assert.That(entity.TimeWithScale, Is.Not.EqualTo(baseTime)); + + int id; + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + await (s.SaveAsync(entity)); + id = entity.Id; + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = await (s.LoadAsync(id)); + Assert.That(retrieved.TimeWithScale.TimeOfDay, Is.EqualTo(baseTime.TimeOfDay)); + await (t.CommitAsync()); + } + } + } +} diff --git a/src/NHibernate.Test/Async/TypesTest/TimestampTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/TimestampTypeFixture.cs index 788986b18cc..39a908c93ee 100644 --- a/src/NHibernate.Test/Async/TypesTest/TimestampTypeFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/TimestampTypeFixture.cs @@ -15,30 +15,11 @@ namespace NHibernate.Test.TypesTest { using System.Threading.Tasks; - using System.Threading; - /// - /// Summary description for TimestampTypeFixture. - /// [TestFixture] - public class TimestampTypeFixtureAsync + [Obsolete] + public class TimestampTypeFixtureAsync : AbstractDateTimeTypeFixtureAsync { - [Test] - public async Task NextAsync() - { - TimestampType type = (TimestampType) NHibernateUtil.Timestamp; - object current = DateTime.Parse("2004-01-01"); - object next = await (type.NextAsync(current, null, CancellationToken.None)); - - Assert.IsTrue(next is DateTime, "Next should be DateTime"); - Assert.IsTrue((DateTime) next > (DateTime) current, - "next should be greater than current (could be equal depending on how quickly this occurs)"); - } - - [Test] - public async Task SeedAsync() - { - TimestampType type = (TimestampType) NHibernateUtil.Timestamp; - Assert.IsTrue(await (type.SeedAsync(null, CancellationToken.None)) is DateTime, "seed should be DateTime"); - } + protected override string TypeName => "Timestamp"; + protected override AbstractDateTimeType Type => NHibernateUtil.Timestamp; } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/TypesTest/TimestampUtcTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/TimestampUtcTypeFixture.cs deleted file mode 100644 index aaf988f8320..00000000000 --- a/src/NHibernate.Test/Async/TypesTest/TimestampUtcTypeFixture.cs +++ /dev/null @@ -1,148 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by AsyncGenerator. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - - -using System; -using NHibernate.Type; -using NUnit.Framework; - -namespace NHibernate.Test.TypesTest -{ - using System.Threading.Tasks; - using System.Threading; - /// - /// Test fixture for type . - /// - [TestFixture] - public class TimestampUtcTypeFixtureAsync : TypeFixtureBase - { - readonly TimestampUtcType _type = NHibernateUtil.TimestampUtc; - readonly DateTime _utc = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Utc); - readonly DateTime _local = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Local); - readonly DateTime _unspecified = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Unspecified); - - /// - /// 1976-11-30T10:00:00.3000000 - /// - const long DateInTicks = 623537928003000000; - - protected override string TypeName => "TimestampUtc"; - - [Test] - public async Task NextAsync() - { - var current = DateTime.Parse("2004-01-01"); - var next = (DateTime)await (_type.NextAsync(current, null, CancellationToken.None)); - - Assert.AreEqual(DateTimeKind.Utc, next.Kind, "Kind is not Utc"); - Assert.IsTrue(next > current, "next should be greater than current (could be equal depending on how quickly this occurs)"); - } - - /// - /// Perform a 'seed' and check if the result is a datetime with kind set to Utc. - /// - [Test] - public async Task SeedAsync() - { - var type = NHibernateUtil.TimestampUtc; - Assert.IsTrue(await (type.SeedAsync(null, CancellationToken.None)) is DateTime, "Seed should be DateTime"); - - var value = (DateTime)await (type.SeedAsync(null, CancellationToken.None)); - Assert.AreEqual(DateTimeKind.Utc, value.Kind, "Kind should be Utc"); - } - - /// - /// Perform a basis write with a DateTime value where Kind is Local which should fail. - /// - [Test] - [TestCase(DateTimeKind.Unspecified)] - [TestCase(DateTimeKind.Local)] - public async Task LocalReadWrite_FailAsync(DateTimeKind kind) - { - var entity = new TimestampUtcClass - { - Id = 1, - Value = DateTime.SpecifyKind(DateTime.Now, kind) - }; - - using(var session = OpenSession()) - using(var tx = session.BeginTransaction()) - { - await (session.SaveAsync(entity)); - Assert.That(() => session.FlushAsync(), Throws.TypeOf()); - await (tx.RollbackAsync()); - } - } - - /// - /// Create two session. Write entity in the first and read it in the second and compare if - /// the retrieved timestamp value still equals the original value. - /// - /// This test takes the database precision into consideration. - [Test] - public async Task UtcReadWrite_SuccessAsync() - { - TimestampUtcClass entity; - - // Save - using(var session = OpenSession()) - using(var tx = session.BeginTransaction()) - { - // Create a new datetime value and round it to the precision that the database supports. This - // code basically the same as in the implementation but here to guard posible changes. - var resolution = session.GetSessionImplementation().Factory.Dialect.TimestampResolutionInTicks; - var next = DateTime.UtcNow; - next = next.AddTicks(-(next.Ticks % resolution)); - - entity = new TimestampUtcClass - { - Id = 1, - Value = next - }; - - await (session.SaveAsync(entity)); - await (tx.CommitAsync()); - session.Close(); - } - - // Retrieve and compare - using (var session = OpenSession()) - using (var tx = session.BeginTransaction()) - { - var result = await (session.GetAsync(entity.Id)); - Assert.IsNotNull(result, "Entity not saved or cannot be retrieved by its key."); - - // Property: Value - Assert.AreEqual(DateTimeKind.Utc, result.Value.Kind, "Kind is NOT Utc"); - Assert.AreEqual(entity.Value.Ticks, result.Value.Ticks, "Value should be the same."); - - // Property: Revision - var revision = result.Revision; - Assert.AreEqual(DateTimeKind.Utc, revision.Kind, "Kind is NOT Utc"); - - var differenceInMinutes = Math.Abs((revision - DateTime.UtcNow).TotalMinutes); - // Take a wide margin for accounting for sometimes bad build servers performances. - Assert.Less(differenceInMinutes, 2, "Difference should be less than 2 minutes."); - - await (tx.CommitAsync()); - session.Close(); - } - - // Delete - using (var session = OpenSession()) - using (var tx = session.BeginTransaction()) - { - var result = await (session.GetAsync(entity.Id)); - await (session.DeleteAsync(result)); - await (tx.CommitAsync()); - session.Close(); - } - } - } -} diff --git a/src/NHibernate.Test/Async/TypesTest/UtcDateTimeTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/UtcDateTimeTypeFixture.cs index 76e97ce5800..1e76aef6d74 100644 --- a/src/NHibernate.Test/Async/TypesTest/UtcDateTimeTypeFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/UtcDateTimeTypeFixture.cs @@ -8,7 +8,7 @@ //------------------------------------------------------------------------------ -using System; +using NHibernate.Type; using NUnit.Framework; namespace NHibernate.Test.TypesTest @@ -18,37 +18,23 @@ namespace NHibernate.Test.TypesTest /// The Unit Tests for the UtcDateTimeType. /// [TestFixture] - public class UtcDateTimeTypeFixtureAsync : TypeFixtureBase + public class UtcDateTimeTypeFixtureAsync : AbstractDateTimeTypeFixtureAsync { - protected override string TypeName - { - get { return "DateTime"; } - } - - [Test] - public async Task ReadWriteAsync() - { - DateTime val = DateTime.UtcNow; - DateTime expected = new DateTime(val.Year, val.Month, val.Day, val.Hour, val.Minute, val.Second, DateTimeKind.Utc); - - DateTimeClass basic = new DateTimeClass(); - basic.Id = 1; - basic.UtcDateTimeValue = val; - - ISession s = OpenSession(); - await (s.SaveAsync(basic)); - await (s.FlushAsync()); - s.Close(); - - s = OpenSession(); - basic = (DateTimeClass) await (s.LoadAsync(typeof (DateTimeClass), 1)); + protected override string TypeName => "UtcDateTime"; + protected override AbstractDateTimeType Type => NHibernateUtil.UtcDateTime; + } - Assert.AreEqual(DateTimeKind.Utc, basic.UtcDateTimeValue.Value.Kind); - Assert.AreEqual(expected, basic.UtcDateTimeValue.Value); + [TestFixture] + public class UtcDateTimeTypeWithScaleFixtureAsync : DateTimeTypeWithScaleFixtureAsync + { + protected override string TypeName => "UtcDateTimeWithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetUtcDateTimeType(3); + } - await (s.DeleteAsync(basic)); - await (s.FlushAsync()); - s.Close(); - } + [TestFixture] + public class UtcDateTimeNoMsTypeFixtureAsync : DateTimeNoMsTypeFixtureAsync + { + protected override string TypeName => "UtcDateTimeNoMs"; + protected override AbstractDateTimeType Type => NHibernateUtil.UtcDateTimeNoMs; } } diff --git a/src/NHibernate.Test/Async/VersionTest/Db/DbVersionFixture.cs b/src/NHibernate.Test/Async/VersionTest/Db/DbVersionFixture.cs index 5423a5645e6..578162f4541 100644 --- a/src/NHibernate.Test/Async/VersionTest/Db/DbVersionFixture.cs +++ b/src/NHibernate.Test/Async/VersionTest/Db/DbVersionFixture.cs @@ -11,8 +11,6 @@ using System; using System.Collections; using System.Threading; -using NHibernate.Driver; -using NHibernate.Engine; using NUnit.Framework; namespace NHibernate.Test.VersionTest.Db @@ -30,25 +28,6 @@ protected override string MappingsAssembly get { return "NHibernate.Test"; } } - protected override bool AppliesTo(ISessionFactoryImplementor factory) - { - // ODBC driver DateTime handling with SQL Server 2008+ Client is broken and forbids using it as a time stamp - // generated on db-side. - // Due to NH-3895, we have to force the scale on date-time parameters to 3 (3 significant fractional seconds digits) - // when using ODBC + SQL Server 2008+, otherwise DateTime values having milliseconds will be rejected. But the SQL - // Server DateTime does not have actually a one millisecond resolution (it has 3.333), causing ODBC to convert the - // parameter to DateTime2. A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is - // to be transmitted as having 3ms or 7ms and will match if transmitted as a DateTime. But when transmitted as - // DateTime2, it will no more be considered equal, causing the test to be flaky and failing two thirds of tries. - // Example failing update captured with profiler: - // exec sp_executesql N'UPDATE book SET name_column = @P1 WHERE id = @P2 AND version_column = @P3', - // N'@P1 nvarchar(18),@P2 int,@P3 datetime2',N'modified test book',1,'2017-08-02 16:37:16.0630000' - // Setting the scale to 2 still causes failure for two thirds of tries, due to 3ms/7ms being truncated in such case - // with ODBC and SQL Server 2008+ Client, which is rejected bu ODBC. - // (Affects NH-1756 too.) - return !(factory.ConnectionProvider.Driver is OdbcDriver); - } - [Test] public async System.Threading.Tasks.Task CollectionVersionAsync() { @@ -76,7 +55,7 @@ public async System.Threading.Tasks.Task CollectionVersionAsync() await (t.CommitAsync()); s.Close(); - Assert.That(!NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); + Assert.That(!NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); guyTimestamp = guy.Timestamp; Thread.Sleep(1500); @@ -88,7 +67,7 @@ public async System.Threading.Tasks.Task CollectionVersionAsync() await (t.CommitAsync()); s.Close(); - Assert.That(!NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); + Assert.That(!NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); s = OpenSession(); t = s.BeginTransaction(); @@ -121,7 +100,7 @@ public async System.Threading.Tasks.Task CollectionNoVersionAsync() s.Close(); const string ownerVersionWasIncremented = "owner version was incremented ({0:o} => {1:o})"; - Assert.That(NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), + Assert.That(NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), string.Format(ownerVersionWasIncremented, guyTimestamp, guy.Timestamp)); Console.WriteLine(string.Format(ownerVersionWasIncremented, guyTimestamp, guy.Timestamp)); @@ -132,7 +111,7 @@ public async System.Threading.Tasks.Task CollectionNoVersionAsync() await (t.CommitAsync()); s.Close(); - Assert.That(NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), + Assert.That(NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), string.Format(ownerVersionWasIncremented, guyTimestamp, guy.Timestamp)); s = OpenSession(); @@ -143,4 +122,4 @@ public async System.Threading.Tasks.Task CollectionNoVersionAsync() s.Close(); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/DebugSessionFactory.cs b/src/NHibernate.Test/DebugSessionFactory.cs index 943ab97f296..041003c9c74 100644 --- a/src/NHibernate.Test/DebugSessionFactory.cs +++ b/src/NHibernate.Test/DebugSessionFactory.cs @@ -179,6 +179,8 @@ bool IMapping.HasNonIdentifierPropertyNamedId(string className) return ActualFactory.HasNonIdentifierPropertyNamedId(className); } + Dialect.Dialect IMapping.Dialect => ActualFactory.Dialect; + void IDisposable.Dispose() { ActualFactory.Dispose(); @@ -270,8 +272,6 @@ ISession ISessionFactory.GetCurrentSession() ICollection ISessionFactory.DefinedFilterNames => ActualFactory.DefinedFilterNames; - Dialect.Dialect ISessionFactoryImplementor.Dialect => ActualFactory.Dialect; - IInterceptor ISessionFactoryImplementor.Interceptor => ActualFactory.Interceptor; QueryPlanCache ISessionFactoryImplementor.QueryPlanCache => ActualFactory.QueryPlanCache; diff --git a/src/NHibernate.Test/DialectTest/MsSql2008DialectFixture.cs b/src/NHibernate.Test/DialectTest/MsSql2008DialectFixture.cs new file mode 100644 index 00000000000..e02b69dfed2 --- /dev/null +++ b/src/NHibernate.Test/DialectTest/MsSql2008DialectFixture.cs @@ -0,0 +1,167 @@ +using System.Data; +using System.Reflection; +using NHibernate.Cfg; +using NHibernate.Dialect; +using NHibernate.Engine; +using NHibernate.SqlTypes; +using NHibernate.Type; +using NUnit.Framework; +using Environment = NHibernate.Cfg.Environment; + +namespace NHibernate.Test.DialectTest +{ + [TestFixture] + public class MsSql2008DialectFixture + { + [Test] + public void CheckPreSql2008DateTimeTypes() + { + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.SetProperty(Environment.Dialect, typeof(MsSql2005Dialect).FullName); + var mapping = GetMapping(cfg); + AssertSqlType(NHibernateUtil.DateTime, SqlTypeFactory.DateTime, mapping); +#pragma warning disable 618 // Timestamp is obsolete + AssertSqlType(NHibernateUtil.Timestamp, SqlTypeFactory.DateTime, mapping); +#pragma warning restore 618 + AssertSqlType(NHibernateUtil.DbTimestamp, SqlTypeFactory.DateTime, mapping); + AssertSqlType(NHibernateUtil.LocalDateTime, SqlTypeFactory.DateTime, mapping); + AssertSqlType(NHibernateUtil.UtcDateTime, SqlTypeFactory.DateTime, mapping); +#pragma warning disable 618 // DateTime2 is obsolete + AssertSqlType(NHibernateUtil.DateTime2, SqlTypeFactory.DateTime2, mapping); +#pragma warning restore 618 + } + + [Test] + public void CheckPreSql2008DateTimeTypesWithScale() + { + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.SetProperty(Environment.Dialect, typeof(MsSql2005Dialect).FullName); + var mapping = GetMapping(cfg); + AssertSqlType(TypeFactory.GetDateTimeType(0), SqlTypeFactory.GetDateTime(0), mapping); + AssertSqlType(TypeFactory.GetLocalDateTimeType(1), SqlTypeFactory.GetDateTime(1), mapping); + AssertSqlType(TypeFactory.GetUtcDateTimeType(2), SqlTypeFactory.GetDateTime(2), mapping); +#pragma warning disable 618 // DateTime2 is obsolete + AssertSqlType(TypeFactory.GetDateTime2Type(3), SqlTypeFactory.GetDateTime2(3), mapping); +#pragma warning restore 618 + } + + [Test] + public void CheckSql2008DateTimeTypes() + { + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.SetProperty(Environment.Dialect, typeof(MsSql2008Dialect).FullName); + var mapping = GetMapping(cfg); + AssertSqlType(NHibernateUtil.DateTime, SqlTypeFactory.DateTime2, mapping); +#pragma warning disable 618 // Timestamp is obsolete + AssertSqlType(NHibernateUtil.Timestamp, SqlTypeFactory.DateTime2, mapping); +#pragma warning restore 618 + AssertSqlType(NHibernateUtil.DbTimestamp, SqlTypeFactory.DateTime2, mapping); + AssertSqlType(NHibernateUtil.LocalDateTime, SqlTypeFactory.DateTime2, mapping); + AssertSqlType(NHibernateUtil.UtcDateTime, SqlTypeFactory.DateTime2, mapping); +#pragma warning disable 618 // DateTime2 is obsolete + AssertSqlType(NHibernateUtil.DateTime2, SqlTypeFactory.DateTime2, mapping); +#pragma warning restore 618 + } + + [Test] + public void CheckSql2008DateTimeTypesWithScale() + { + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.SetProperty(Environment.Dialect, typeof(MsSql2008Dialect).FullName); + var mapping = GetMapping(cfg); + AssertSqlType(TypeFactory.GetDateTimeType(4), SqlTypeFactory.GetDateTime2(4), mapping); + AssertSqlType(TypeFactory.GetLocalDateTimeType(5), SqlTypeFactory.GetDateTime2(5), mapping); + AssertSqlType(TypeFactory.GetUtcDateTimeType(6), SqlTypeFactory.GetDateTime2(6), mapping); +#pragma warning disable 618 // DateTime2 is obsolete + AssertSqlType(TypeFactory.GetDateTime2Type(7), SqlTypeFactory.GetDateTime2(7), mapping); +#pragma warning restore 618 + } + + [Test] + public void CheckKeepDateTime() + { + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.SetProperty(Environment.Dialect, typeof(MsSql2008Dialect).FullName); + cfg.SetProperty(Environment.SqlTypesKeepDateTime, "true"); + var mapping = GetMapping(cfg); + AssertSqlType(NHibernateUtil.DateTime, SqlTypeFactory.DateTime, mapping); +#pragma warning disable 618 // Timestamp is obsolete + AssertSqlType(NHibernateUtil.Timestamp, SqlTypeFactory.DateTime, mapping); +#pragma warning restore 618 + AssertSqlType(NHibernateUtil.DbTimestamp, SqlTypeFactory.DateTime, mapping); + AssertSqlType(NHibernateUtil.LocalDateTime, SqlTypeFactory.DateTime, mapping); + AssertSqlType(NHibernateUtil.UtcDateTime, SqlTypeFactory.DateTime, mapping); +#pragma warning disable 618 // DateTime2 is obsolete + AssertSqlType(NHibernateUtil.DateTime2, SqlTypeFactory.DateTime2, mapping); +#pragma warning restore 618 + } + + [Test] + public void CheckKeepDateTimeWithScale() + { + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.SetProperty(Environment.Dialect, typeof(MsSql2008Dialect).FullName); + cfg.SetProperty(Environment.SqlTypesKeepDateTime, "true"); + var mapping = GetMapping(cfg); + AssertSqlType(TypeFactory.GetDateTimeType(0), SqlTypeFactory.GetDateTime(0), mapping); + AssertSqlType(TypeFactory.GetLocalDateTimeType(1), SqlTypeFactory.GetDateTime(1), mapping); + AssertSqlType(TypeFactory.GetUtcDateTimeType(2), SqlTypeFactory.GetDateTime(2), mapping); +#pragma warning disable 618 // DateTime2 is obsolete + AssertSqlType(TypeFactory.GetDateTime2Type(3), SqlTypeFactory.GetDateTime2(3), mapping); +#pragma warning restore 618 + } + + [Test] + public void ScaleTypes() + { + const int min = 0; + const int intermediate = 5; + const int max = 7; + var dialect = new MsSql2008Dialect(); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.DateTime), Is.EqualTo("datetime").IgnoreCase, "Default datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(min)), Is.EqualTo("datetime").IgnoreCase, "Min datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(intermediate)), Is.EqualTo("datetime").IgnoreCase, "Intermediate datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max)), Is.EqualTo("datetime").IgnoreCase, "Max datetime"); + Assert.That(dialect.GetLongestTypeName(DbType.DateTime), Is.EqualTo("datetime").IgnoreCase, "Longest datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max + 1)), Is.EqualTo("datetime").IgnoreCase, "Over max datetime"); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.DateTime2), Is.EqualTo("datetime2").IgnoreCase, "Default datetime2"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime2(min)), Is.EqualTo($"datetime2({min})").IgnoreCase, "Min datetime2"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime2(intermediate)), Is.EqualTo($"datetime2({intermediate})").IgnoreCase, "Intermediate datetime2"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime2(max)), Is.EqualTo($"datetime2({max})").IgnoreCase, "Max datetime2"); + Assert.That(dialect.GetLongestTypeName(DbType.DateTime2), Is.EqualTo($"datetime2({max})").IgnoreCase, "Longest datetime2"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime2(max + 1)), Is.EqualTo("datetime2").IgnoreCase, "Over max datetime2"); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.DateTimeOffSet), Is.EqualTo("datetimeoffset").IgnoreCase, "Default datetimeoffset"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTimeOffset(min)), Is.EqualTo($"datetimeoffset({min})").IgnoreCase, "Min datetimeoffset"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTimeOffset(intermediate)), Is.EqualTo($"datetimeoffset({intermediate})").IgnoreCase, "Intermediate datetimeoffset"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTimeOffset(max)), Is.EqualTo($"datetimeoffset({max})").IgnoreCase, "Max datetimeoffset"); + Assert.That(dialect.GetLongestTypeName(DbType.DateTimeOffset), Is.EqualTo($"datetimeoffset({max})").IgnoreCase, "Longest datetimeoffset"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTimeOffset(max + 1)), Is.EqualTo("datetimeoffset").IgnoreCase, "Over max datetimeoffset"); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.Time), Is.EqualTo("time").IgnoreCase, "Default time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(min)), Is.EqualTo($"time({min})").IgnoreCase, "Min time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(intermediate)), Is.EqualTo($"time({intermediate})").IgnoreCase, "Intermediate time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max)), Is.EqualTo($"time({max})").IgnoreCase, "Max time"); + Assert.That(dialect.GetLongestTypeName(DbType.Time), Is.EqualTo($"time({max})").IgnoreCase, "Longest time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max + 1)), Is.EqualTo("time").IgnoreCase, "Over max time"); + } + + private static readonly FieldInfo _mappingField = + typeof(Configuration).GetField("mapping", BindingFlags.Instance | BindingFlags.NonPublic); + + private static IMapping GetMapping(Configuration cfg) + { + Assert.That(_mappingField, Is.Not.Null, "Unable to find field mapping"); + var mapping = _mappingField.GetValue(cfg) as IMapping; + Assert.That(mapping, Is.Not.Null, "Unable to find mapping object"); + return mapping; + } + + private static void AssertSqlType(IType type, SqlType sqlType, IMapping mapping) + { + Assert.That(type.SqlTypes(mapping), Has.Length.EqualTo(1).And.Contain(sqlType), type.Name); + } + } +} diff --git a/src/NHibernate.Test/DialectTest/MsSqlDialectFixture.cs b/src/NHibernate.Test/DialectTest/MsSqlDialectFixture.cs index 738f3e8dfdb..51b66aeca49 100644 --- a/src/NHibernate.Test/DialectTest/MsSqlDialectFixture.cs +++ b/src/NHibernate.Test/DialectTest/MsSqlDialectFixture.cs @@ -63,12 +63,12 @@ public void GetLimitString() } [Test] - public void TimestampRounding() + public void DateTimeRounding() { DateTime input = new DateTime(2000, 1, 1, 10, 11, 12, 13); DateTime expected = new DateTime(2000, 1, 1, 10, 11, 12, 10); - Assert.AreEqual(expected, TimestampType.Round(input, d.TimestampResolutionInTicks)); + Assert.AreEqual(expected, AbstractDateTimeType.Round(input, d.TimestampResolutionInTicks)); } } } \ No newline at end of file diff --git a/src/NHibernate.Test/DialectTest/MySQL57DialectFixture.cs b/src/NHibernate.Test/DialectTest/MySQL57DialectFixture.cs new file mode 100644 index 00000000000..932628c81f6 --- /dev/null +++ b/src/NHibernate.Test/DialectTest/MySQL57DialectFixture.cs @@ -0,0 +1,34 @@ +using System.Data; +using NHibernate.Dialect; +using NHibernate.SqlTypes; +using NUnit.Framework; + +namespace NHibernate.Test.DialectTest +{ + [TestFixture] + public class MySQL57DialectFixture + { + [Test] + public void ScaleTypes() + { + const int min = 0; + const int intermediate = 4; + const int max = 6; + var dialect = new MySQL57Dialect(); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.DateTime), Is.EqualTo($"datetime({max})").IgnoreCase, "Default datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(min)), Is.EqualTo($"datetime({min})").IgnoreCase, "Min datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(intermediate)), Is.EqualTo($"datetime({intermediate})").IgnoreCase, "Intermediate datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max)), Is.EqualTo($"datetime({max})").IgnoreCase, "Max datetime"); + Assert.That(dialect.GetLongestTypeName(DbType.DateTime), Is.EqualTo($"datetime({max})").IgnoreCase, "Longest datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max + 1)), Is.EqualTo($"datetime({max})").IgnoreCase, "Over max datetime"); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.Time), Is.EqualTo($"time({max})").IgnoreCase, "Default time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(min)), Is.EqualTo($"time({min})").IgnoreCase, "Min time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(intermediate)), Is.EqualTo($"time({intermediate})").IgnoreCase, "Intermediate time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max)), Is.EqualTo($"time({max})").IgnoreCase, "Max time"); + Assert.That(dialect.GetLongestTypeName(DbType.Time), Is.EqualTo($"time({max})").IgnoreCase, "Longest time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max + 1)), Is.EqualTo($"time({max})").IgnoreCase, "Over max time"); + } + } +} diff --git a/src/NHibernate.Test/DialectTest/Oracle9iDialectFixture.cs b/src/NHibernate.Test/DialectTest/Oracle9iDialectFixture.cs new file mode 100644 index 00000000000..49c51faab09 --- /dev/null +++ b/src/NHibernate.Test/DialectTest/Oracle9iDialectFixture.cs @@ -0,0 +1,35 @@ +using System.Data; +using NHibernate.Dialect; +using NHibernate.SqlTypes; +using NUnit.Framework; + +namespace NHibernate.Test.DialectTest +{ + [TestFixture] + public class Oracl9iDialectFixture + { + [Test] + public void ScaleTypes() + { + const int @default = 7; + const int min = 0; + const int intermediate = 5; + const int max = 9; + var dialect = new Oracle9iDialect(); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.DateTime), Is.EqualTo($"timestamp({@default})").IgnoreCase, "Default datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(min)), Is.EqualTo($"timestamp({min})").IgnoreCase, "Min datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(intermediate)), Is.EqualTo($"timestamp({intermediate})").IgnoreCase, "Intermediate datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max)), Is.EqualTo($"timestamp({max})").IgnoreCase, "Max datetime"); + Assert.That(dialect.GetLongestTypeName(DbType.DateTime), Is.EqualTo($"timestamp({max})").IgnoreCase, "Longest datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max + 1)), Is.EqualTo($"timestamp({@default})").IgnoreCase, "Over max datetime"); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.Time), Is.EqualTo($"timestamp({@default})").IgnoreCase, "Default time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(min)), Is.EqualTo($"timestamp({min})").IgnoreCase, "Min time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(intermediate)), Is.EqualTo($"timestamp({intermediate})").IgnoreCase, "Intermediate time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max)), Is.EqualTo($"timestamp({max})").IgnoreCase, "Max time"); + Assert.That(dialect.GetLongestTypeName(DbType.Time), Is.EqualTo($"timestamp({max})").IgnoreCase, "Longest time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max + 1)), Is.EqualTo($"timestamp({@default})").IgnoreCase, "Over max time"); + } + } +} diff --git a/src/NHibernate.Test/DialectTest/PostgreSQL81DialectFixture.cs b/src/NHibernate.Test/DialectTest/PostgreSQL81DialectFixture.cs new file mode 100644 index 00000000000..6b869824329 --- /dev/null +++ b/src/NHibernate.Test/DialectTest/PostgreSQL81DialectFixture.cs @@ -0,0 +1,34 @@ +using System.Data; +using NHibernate.Dialect; +using NHibernate.SqlTypes; +using NUnit.Framework; + +namespace NHibernate.Test.DialectTest +{ + [TestFixture] + public class PostgreSQL81DialectFixture + { + [Test] + public void ScaleTypes() + { + const int min = 0; + const int intermediate = 4; + const int max = 6; + var dialect = new PostgreSQL81Dialect(); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.DateTime), Is.EqualTo("timestamp").IgnoreCase, "Default datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(min)), Is.EqualTo($"timestamp({min})").IgnoreCase, "Min datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(intermediate)), Is.EqualTo($"timestamp({intermediate})").IgnoreCase, "Intermediate datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max)), Is.EqualTo($"timestamp({max})").IgnoreCase, "Max datetime"); + Assert.That(dialect.GetLongestTypeName(DbType.DateTime), Is.EqualTo($"timestamp({max})").IgnoreCase, "Longest datetime"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetDateTime(max + 1)), Is.EqualTo("timestamp").IgnoreCase, "Over max datetime"); + + Assert.That(dialect.GetTypeName(SqlTypeFactory.Time), Is.EqualTo($"time").IgnoreCase, "Default time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(min)), Is.EqualTo($"time({min})").IgnoreCase, "Min time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(intermediate)), Is.EqualTo($"time({intermediate})").IgnoreCase, "Intermediate time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max)), Is.EqualTo($"time({max})").IgnoreCase, "Max time"); + Assert.That(dialect.GetLongestTypeName(DbType.Time), Is.EqualTo($"time({max})").IgnoreCase, "Longest time"); + Assert.That(dialect.GetTypeName(SqlTypeFactory.GetTime(max + 1)), Is.EqualTo($"time").IgnoreCase, "Over max time"); + } + } +} diff --git a/src/NHibernate.Test/Legacy/CriteriaTest.cs b/src/NHibernate.Test/Legacy/CriteriaTest.cs index e7e2c46449f..4b28d9779f1 100644 --- a/src/NHibernate.Test/Legacy/CriteriaTest.cs +++ b/src/NHibernate.Test/Legacy/CriteriaTest.cs @@ -29,14 +29,14 @@ public void SimpleSelectTest() long simple1Key = 15; Simple simple1 = new Simple(); simple1.Address = "Street 12"; - simple1.Date = DateTime.Now; + simple1.Date = RoundForDialect(DateTime.Now); simple1.Name = "For Criteria Test"; simple1.Count = 16; long notSimple1Key = 17; Simple notSimple1 = new Simple(); notSimple1.Address = "Street 123"; - notSimple1.Date = DateTime.Now; + notSimple1.Date = RoundForDialect(DateTime.Now); notSimple1.Name = "Don't be found"; notSimple1.Count = 18; @@ -51,19 +51,19 @@ public void SimpleSelectTest() using (ISession s2 = OpenSession()) using (ITransaction t2 = s2.BeginTransaction()) { - IList results2 = s2.CreateCriteria(typeof(Simple)) - .Add(Expression.Eq("Address", "Street 12")) - .List(); + var results2 = s2.CreateCriteria() + .Add(Restrictions.Eq("Address", "Street 12")) + .List(); - Assert.AreEqual(1, results2.Count); + Assert.That(results2.Count, Is.EqualTo(1), "Unexpected result count"); - Simple simple2 = (Simple) results2[0]; + var simple2 = results2[0]; - Assert.IsNotNull(simple2, "Unable to load object"); - Assert.AreEqual(simple1.Count, simple2.Count, "Load failed"); - Assert.AreEqual(simple1.Name, simple2.Name, "Load failed"); - Assert.AreEqual(simple1.Address, simple2.Address, "Load failed"); - Assert.AreEqual(simple1.Date.ToString(), simple2.Date.ToString(), "Load failed"); + Assert.That(simple2, Is.Not.Null, "Unable to load object"); + Assert.That(simple2.Count, Is.EqualTo(simple1.Count), "Unexpected Count property value"); + Assert.That(simple2.Name, Is.EqualTo(simple1.Name), "Unexpected name"); + Assert.That(simple2.Address, Is.EqualTo(simple1.Address), "Unexpected address"); + Assert.That(simple2.Date, Is.EqualTo(simple1.Date), "Unexpected date"); s2.Delete("from Simple"); @@ -189,4 +189,4 @@ public void DetachedCriteria_can_get_query_entity_type() } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Legacy/SQLLoaderTest.cs b/src/NHibernate.Test/Legacy/SQLLoaderTest.cs index d3c92c448ca..eaa4a5c579d 100644 --- a/src/NHibernate.Test/Legacy/SQLLoaderTest.cs +++ b/src/NHibernate.Test/Legacy/SQLLoaderTest.cs @@ -48,7 +48,7 @@ public void TS() session.Save(sim, 1L); IQuery q = session.CreateSQLQuery("select {sim.*} from Simple {sim} where {sim}.date_ = ?") .AddEntity("sim", typeof(Simple)); - q.SetTimestamp(0, sim.Date); + q.SetDateTime(0, sim.Date); Assert.AreEqual(1, q.List().Count, "q.List.Count"); session.Delete(sim); txn.Commit(); @@ -72,7 +72,7 @@ public void TSNamed() IQuery q = session.CreateSQLQuery("select {sim.*} from Simple {sim} where {sim}.date_ = :fred") .AddEntity("sim", typeof(Simple)); - q.SetTimestamp("fred", sim.Date); + q.SetDateTime("fred", sim.Date); Assert.AreEqual(1, q.List().Count, "q.List.Count"); session.Delete(sim); txn.Commit(); @@ -648,4 +648,4 @@ public void NamedSQLQuery() s.Close(); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH1756/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1756/Fixture.cs index 76bf79108fc..8098a37ca7a 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1756/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH1756/Fixture.cs @@ -17,19 +17,19 @@ protected override bool AppliesTo(Dialect.Dialect dialect) protected override bool AppliesTo(ISessionFactoryImplementor factory) { // ODBC driver DateTime handling with SQL Server 2008+ Client is broken and forbids using it as a time stamp - // generated on db-side. + // custom generated on db-side. // Due to NH-3895, we have to force the scale on date-time parameters to 3 (3 significant fractional seconds digits) // when using ODBC + SQL Server 2008+, otherwise DateTime values having milliseconds will be rejected. But the SQL // Server DateTime does not have actually a one millisecond resolution (it has 3.333), causing ODBC to convert the - // parameter to DateTime2. A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is + // parameter to DateTime2 (yes it supports it, only its .Net classes do not). + // A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is // to be transmitted as having 3ms or 7ms and will match if transmitted as a DateTime. But when transmitted as // DateTime2, it will no more be considered equal, causing the test to be flaky and failing two thirds of tries. // Example failing update captured with profiler: // exec sp_executesql N'UPDATE book SET name_column = @P1 WHERE id = @P2 AND version_column = @P3', // N'@P1 nvarchar(18),@P2 int,@P3 datetime2',N'modified test book',1,'2017-08-02 16:37:16.0630000' // Setting the scale to 2 still causes failure for two thirds of tries, due to 3ms/7ms being truncated in such case - // with ODBC and SQL Server 2008+ Client, which is rejected bu ODBC. - // (Affects DbVersionFixture too.) + // with ODBC and SQL Server 2008+ Client, which is rejected by ODBC. return !(factory.ConnectionProvider.Driver is OdbcDriver); } @@ -104,4 +104,4 @@ public void SaveTransient_Then_Update_Bug() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH283/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH283/Fixture.cs index 92d6c7a6f08..89154acca71 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH283/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH283/Fixture.cs @@ -1,4 +1,3 @@ -using System; using System.Reflection; using NHibernate.Cfg; using NHibernate.Dialect; @@ -21,11 +20,11 @@ public void ForeignKeyNames() string script = string.Join("\n", - cfg.GenerateSchemaCreationScript(new MsSql2000Dialect())); + cfg.GenerateSchemaCreationScript(new MsSql2008Dialect())); Assert.IsTrue(script.IndexOf("add constraint AA") >= 0); Assert.IsTrue(script.IndexOf("add constraint BB") >= 0); Assert.IsTrue(script.IndexOf("add constraint CC") >= 0); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs index 455a03766cb..696ff761781 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs @@ -118,7 +118,7 @@ protected override HbmMapping GetMappings() rc.Property(x => x.Name); rc.Property(x => x.DateOfBirth, pm => { - pm.Type(NHibernateUtil.Timestamp); + pm.Type(NHibernateUtil.DateTime); }); }); diff --git a/src/NHibernate.Test/NHSpecificTest/NH3567/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH3567/Mappings.hbm.xml index 00865d09e6e..1b36396fd18 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3567/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/NH3567/Mappings.hbm.xml @@ -25,4 +25,4 @@ - \ No newline at end of file + diff --git a/src/NHibernate.Test/NHSpecificTest/NH3818/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3818/Fixture.cs index b1a0570a430..e8ef6797db5 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3818/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3818/Fixture.cs @@ -1,22 +1,16 @@ using System; using System.Linq; -using System.Linq.Dynamic; -using NHibernate.Linq; using NUnit.Framework; namespace NHibernate.Test.NHSpecificTest.NH3818 { - [TestFixture] - public class Fixture : BugTestCase - { - public override string BugNumber - { - get { return "NH3818"; } - } - + [TestFixture] + public class Fixture : BugTestCase + { [Test] public void SelectConditionalValuesTest() { + var now = RoundForDialect(DateTime.Now); using (var spy = new SqlLogSpy()) using (var session = OpenSession()) using (session.BeginTransaction()) @@ -26,7 +20,7 @@ public void SelectConditionalValuesTest() var cat = new MyLovelyCat { GUID = Guid.NewGuid(), - Birthdate = DateTime.Now.AddDays(-days), + Birthdate = now.AddDays(-days), Color = "Black", Name = "Kitty", Price = 0 @@ -40,31 +34,33 @@ public void SelectConditionalValuesTest() .Select(o => new { o.Color, - AliveDays = (int)(DateTime.Now - o.Birthdate).TotalDays, + AliveDays = (now - o.Birthdate).TotalDays, o.Name, o.Price, }) .Single(); //Console.WriteLine(spy.ToString()); - Assert.That(catInfo.AliveDays == days); + // We need a tolerance: we are diffing dates as a timespan instead of counting days boundaries, + // yielding a float. TimeSpan.Days yould always truncate a resulting partial day, so do not use it. + Assert.That(catInfo.AliveDays, Is.EqualTo(days).Within(0.1)); var catInfo2 = session.Query() .Select(o => new { o.Color, - AliveDays = o.Price > 0 ? (DateTime.Now - o.Birthdate).TotalDays : 0, + AliveDays = o.Price > 0 ? (now - o.Birthdate).TotalDays : 0, o.Name, o.Price, }) .Single(); //Console.WriteLine(spy.ToString()); - Assert.That(catInfo2.AliveDays == 0); + Assert.That(catInfo2.AliveDays, Is.EqualTo(0)); } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH392/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH392/Fixture.cs index 83b2f7c78be..31beca5cc42 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH392/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH392/Fixture.cs @@ -35,15 +35,15 @@ public void UnsavedMinusOneNoNullReferenceException() } } - protected override void OnTearDown() + protected override void OnTearDown() { using (ISession s = Sfi.OpenSession()) { // s.Delete("from UnsavedValueMinusOne") loads then delete entities one by one, checking the version. - // This fails with ODBC & Sql Server 2008+, see NH-1756 test case or DbVersionFixture for more details. + // This fails with ODBC & Sql Server 2008+, see NH-1756 test case for more details. // Use an in-db query instead. s.CreateQuery("delete from UnsavedValueMinusOne").ExecuteUpdate(); } - } + } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3961/DateParametersComparedTo.cs b/src/NHibernate.Test/NHSpecificTest/NH3961/DateParametersComparedTo.cs index 678e90c8502..b7317857cd9 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3961/DateParametersComparedTo.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3961/DateParametersComparedTo.cs @@ -103,6 +103,7 @@ public void NonNullableMappedAsDateTimeShouldBeCultureAgnostic() // Non-reg test case [Test] + [Obsolete] public void NonNullableMappedAsTimestampShouldBeCultureAgnostic() { using (ISession session = OpenSession()) @@ -216,6 +217,7 @@ public void NullableMappedAsDateTimeShouldBeCultureAgnostic() // Failing test case till NH-3961 is fixed [Test] + [Obsolete] public void NullableMappedAsTimestampShouldBeCultureAgnostic() { using (ISession session = OpenSession()) @@ -279,4 +281,4 @@ public void NullableShouldBeCultureAgnostic() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3984/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3984/Fixture.cs new file mode 100644 index 00000000000..0343301b27f --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3984/Fixture.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using NHibernate.Dialect; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH3984 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnTearDown() + { + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateSQLQuery("delete from LogEntry").ExecuteUpdate(); + tx.Commit(); + } + } + + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return dialect is MsSql2008Dialect; + } + + [Test] + public void ShouldBePossibleToReadMillisecondsFromDatetime2ViaCreateSQLQuery() + { + var now = DateTime.UtcNow; + + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var entry = new LogEntry { Text = "Test", CreatedAt = now }; + session.Save(entry); + + session.Flush(); + transaction.Commit(); + } + + using (var session = OpenSession()) + { + var datetimeViaQuery = session + .Query() + .Select(x => x.CreatedAt) + .Single(); + + Assert.That(datetimeViaQuery.Millisecond, Is.EqualTo(now.Millisecond)); + + var datetimeViaCreateSqlQuery = session + .CreateSQLQuery("SELECT CreatedAt FROM LogEntry") + .List() + .Cast() + .Single(); + + Assert.That(datetimeViaCreateSqlQuery.Millisecond, Is.EqualTo(now.Millisecond)); + } + } + + [Test] + public void ShouldBePossibleToReadTicksFromDatetime2ViaCreateSQLQuery() + { + var now = DateTime.Now; + + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var entry = new LogEntry { Text = "Test", CreatedAt = now }; + session.Save(entry); + + session.Flush(); + transaction.Commit(); + } + + using (var session = OpenSession()) + { + var datetimeViaQuery = session + .Query() + .Select(x => x.CreatedAt) + .Single(); + + Assert.That(datetimeViaQuery.Ticks, Is.EqualTo(now.Ticks)); + + var datetimeViaCreateSqlQuery = session + .CreateSQLQuery("SELECT CreatedAt FROM LogEntry") + .List() + .Cast() + .Single(); + + Assert.That(datetimeViaCreateSqlQuery.Ticks, Is.EqualTo(now.Ticks)); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3984/LogEntry.cs b/src/NHibernate.Test/NHSpecificTest/NH3984/LogEntry.cs new file mode 100644 index 00000000000..e5a2c9c6c25 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3984/LogEntry.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.NH3984 +{ + public class LogEntry + { + public virtual int Id { get; set; } + public virtual string Text { get; set; } + public virtual DateTime CreatedAt { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3984/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH3984/Mappings.hbm.xml new file mode 100644 index 00000000000..57d48ade8d5 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3984/Mappings.hbm.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index 6ec37b423d6..1ccfb066999 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -1,18 +1,14 @@  - The Unit Tests for NHibernate. - net461 $(NoWarn);3001;3002;3003;3005 - True - @@ -20,7 +16,6 @@ Always - @@ -32,13 +27,11 @@ Always - Always - @@ -51,20 +44,16 @@ - - - - - + \ No newline at end of file diff --git a/src/NHibernate.Test/NHibernateUtilTest.cs b/src/NHibernate.Test/NHibernateUtilTest.cs index 5d43558d93a..438e9ef0e48 100644 --- a/src/NHibernate.Test/NHibernateUtilTest.cs +++ b/src/NHibernate.Test/NHibernateUtilTest.cs @@ -1,3 +1,5 @@ +using System; + namespace NHibernate.Test { using NUnit.Framework; @@ -25,7 +27,6 @@ public void CanGuessTypeOfNullableInt32ByType() NHibernateUtil.GuessType(typeof(int?))); } - [Test] public void CanGuessTypeOfNullableInt32ByValue() { @@ -33,5 +34,12 @@ public void CanGuessTypeOfNullableInt32ByValue() Assert.AreEqual(NHibernateUtil.Int32, NHibernateUtil.GuessType(val)); } + + [Test] + public void CanGuessTypeOfDateTime() + { + Assert.AreEqual(NHibernateUtil.DateTime, + NHibernateUtil.GuessType(typeof(DateTime))); + } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Operations/MergeFixture.cs b/src/NHibernate.Test/Operations/MergeFixture.cs index 0ad89cce5de..201d3ee7233 100644 --- a/src/NHibernate.Test/Operations/MergeFixture.cs +++ b/src/NHibernate.Test/Operations/MergeFixture.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NHibernate.Criterion; using NUnit.Framework; @@ -151,9 +152,21 @@ public void MergeDeepTree() { ClearCounts(); - var root = new Node {Name = "root"}; - var child = new Node {Name = "child"}; - var grandchild = new Node {Name = "grandchild"}; + var root = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "root" + }; + var child = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child" + }; + var grandchild = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "grandchild" + }; using (ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) @@ -169,7 +182,11 @@ public void MergeDeepTree() ClearCounts(); grandchild.Description = "the grand child"; - var grandchild2 = new Node {Name = "grandchild2"}; + var grandchild2 = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "grandchild2" + }; child.AddChild(grandchild2); using (var s = OpenSession()) @@ -183,8 +200,16 @@ public void MergeDeepTree() AssertUpdateCount(1); ClearCounts(); - var child2 = new Node {Name = "child2"}; - var grandchild3 = new Node {Name = "grandchild3"}; + var child2 = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child2" + }; + var grandchild3 = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "grandchild3" + }; child2.AddChild(grandchild3); root.AddChild(child2); @@ -223,9 +248,9 @@ public void MergeDeepTreeWithGeneratedId() using (ISession s = OpenSession()) { ITransaction tx = s.BeginTransaction(); - root = new NumberedNode("root"); - child = new NumberedNode("child"); - grandchild = new NumberedNode("grandchild"); + root = new NumberedNode("root", RoundForDialect(DateTime.Now)); + child = new NumberedNode("child", RoundForDialect(DateTime.Now)); + grandchild = new NumberedNode("grandchild", RoundForDialect(DateTime.Now)); root.AddChild(child); child.AddChild(grandchild); root = (NumberedNode) s.Merge(root); @@ -243,7 +268,7 @@ public void MergeDeepTreeWithGeneratedId() cit.MoveNext(); grandchild = cit.Current; grandchild.Description = "the grand child"; - var grandchild2 = new NumberedNode("grandchild2"); + var grandchild2 = new NumberedNode("grandchild2", RoundForDialect(DateTime.Now)); child.AddChild(grandchild2); using (ISession s = OpenSession()) @@ -259,8 +284,8 @@ public void MergeDeepTreeWithGeneratedId() Sfi.Evict(typeof (NumberedNode)); - var child2 = new NumberedNode("child2"); - var grandchild3 = new NumberedNode("grandchild3"); + var child2 = new NumberedNode("child2", RoundForDialect(DateTime.Now)); + var grandchild3 = new NumberedNode("grandchild3", RoundForDialect(DateTime.Now)); child2.AddChild(grandchild3); root.AddChild(child2); @@ -293,7 +318,7 @@ public void MergeManaged() NumberedNode root; using (ITransaction tx = s.BeginTransaction()) { - root = new NumberedNode("root"); + root = new NumberedNode("root", RoundForDialect(DateTime.Now)); s.Persist(root); tx.Commit(); } @@ -302,7 +327,7 @@ public void MergeManaged() NumberedNode mergedChild; using (var tx = s.BeginTransaction()) { - var child = new NumberedNode("child"); + var child = new NumberedNode("child", RoundForDialect(DateTime.Now)); root.AddChild(child); Assert.That(s.Merge(root), Is.SameAs(root)); IEnumerator rit = root.Children.GetEnumerator(); @@ -432,8 +457,16 @@ public void MergeTree() { ClearCounts(); - var root = new Node {Name = "root"}; - var child = new Node {Name = "child"}; + var root = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "root" + }; + var child = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child" + }; using(ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) { @@ -448,7 +481,11 @@ public void MergeTree() root.Description = "The root node"; child.Description = "The child node"; - var secondChild = new Node {Name = "second child"}; + var secondChild = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "second child" + }; root.AddChild(secondChild); @@ -468,8 +505,8 @@ public void MergeTreeWithGeneratedId() { ClearCounts(); - var root = new NumberedNode("root"); - var child = new NumberedNode("child"); + var root = new NumberedNode("root", RoundForDialect(DateTime.Now)); + var child = new NumberedNode("child", RoundForDialect(DateTime.Now)); using(ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) { @@ -484,7 +521,7 @@ public void MergeTreeWithGeneratedId() root.Description = "The root node"; child.Description = "The child node"; - var secondChild = new NumberedNode("second child"); + var secondChild = new NumberedNode("second child", RoundForDialect(DateTime.Now)); root.AddChild(secondChild); @@ -502,7 +539,11 @@ public void MergeTreeWithGeneratedId() [Test] public void NoExtraUpdatesOnMerge() { - var node = new Node {Name = "test"}; + var node = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "test" + }; using(ISession s = OpenSession()) using (s.BeginTransaction()) { @@ -635,11 +676,19 @@ public void NoExtraUpdatesOnMergeVersionedWithCollection() [Test] public void NoExtraUpdatesOnMergeWithCollection() { - var parent = new Node {Name = "parent"}; + var parent = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "parent" + }; using(ISession s = OpenSession()) using (s.BeginTransaction()) { - var child = new Node {Name = "child"}; + var child = new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "child" + }; parent.Children.Add(child); child.Parent = parent; s.Persist(parent); @@ -666,7 +715,12 @@ public void NoExtraUpdatesOnMergeWithCollection() IEnumerator it = parent.Children.GetEnumerator(); it.MoveNext(); it.Current.Description = "child's new description"; - parent.Children.Add(new Node {Name = "second child"}); + parent.Children.Add( + new Node + { + Created = RoundForDialect(DateTime.Now), + Name = "second child" + }); using(var s = OpenSession()) using (s.BeginTransaction()) { diff --git a/src/NHibernate.Test/Operations/NumberedNode.cs b/src/NHibernate.Test/Operations/NumberedNode.cs index 3bd41f1b7cb..e551bb05497 100644 --- a/src/NHibernate.Test/Operations/NumberedNode.cs +++ b/src/NHibernate.Test/Operations/NumberedNode.cs @@ -9,10 +9,10 @@ public class NumberedNode protected NumberedNode() {} - public NumberedNode(string name) + public NumberedNode(string name, DateTime created) { Name = name; - Created = DateTime.Now; + Created = created; } public virtual long Id { get; set; } @@ -34,4 +34,4 @@ public virtual NumberedNode AddChild(NumberedNode child) return this; } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/QueryTest/DetachedQueryFixture.cs b/src/NHibernate.Test/QueryTest/DetachedQueryFixture.cs index f156778901d..32ab71aa270 100644 --- a/src/NHibernate.Test/QueryTest/DetachedQueryFixture.cs +++ b/src/NHibernate.Test/QueryTest/DetachedQueryFixture.cs @@ -97,7 +97,7 @@ public void PropertiesSet() tdq.SetSingle(14, 1.1f); tdq.SetString(15, ""); tdq.SetTime(16, DateTime.Now); - tdq.SetTimestamp(17, DateTime.Now); + tdq.SetDateTimeNoMs(17, DateTime.Now); tdq.SetGuid(18, Guid.Empty); Assert.IsTrue(tdq.PosParams[1].Type.Equals(NHibernateUtil.AnsiString)); Assert.IsTrue(tdq.PosParams[2].Type.Equals(NHibernateUtil.Binary)); @@ -115,7 +115,7 @@ public void PropertiesSet() Assert.IsTrue(tdq.PosParams[14].Type.Equals(NHibernateUtil.Single)); Assert.IsTrue(tdq.PosParams[15].Type.Equals(NHibernateUtil.String)); Assert.IsTrue(tdq.PosParams[16].Type.Equals(NHibernateUtil.Time)); - Assert.IsTrue(tdq.PosParams[17].Type.Equals(NHibernateUtil.Timestamp)); + Assert.IsTrue(tdq.PosParams[17].Type.Equals(NHibernateUtil.DateTimeNoMs)); Assert.IsTrue(tdq.PosParams[18].Type.Equals(NHibernateUtil.Guid)); tdq.SetAnsiString("1", ""); @@ -134,7 +134,7 @@ public void PropertiesSet() tdq.SetSingle("14", 1.1f); tdq.SetString("15", ""); tdq.SetTime("16", DateTime.Now); - tdq.SetTimestamp("17", DateTime.Now); + tdq.SetDateTimeNoMs("17", DateTime.Now); tdq.SetGuid("18", Guid.Empty); Assert.IsTrue(tdq.NamedParams["1"].Type.Equals(NHibernateUtil.AnsiString)); Assert.IsTrue(tdq.NamedParams["2"].Type.Equals(NHibernateUtil.Binary)); @@ -152,7 +152,7 @@ public void PropertiesSet() Assert.IsTrue(tdq.NamedParams["14"].Type.Equals(NHibernateUtil.Single)); Assert.IsTrue(tdq.NamedParams["15"].Type.Equals(NHibernateUtil.String)); Assert.IsTrue(tdq.NamedParams["16"].Type.Equals(NHibernateUtil.Time)); - Assert.IsTrue(tdq.NamedParams["17"].Type.Equals(NHibernateUtil.Timestamp)); + Assert.IsTrue(tdq.NamedParams["17"].Type.Equals(NHibernateUtil.DateTimeNoMs)); Assert.IsTrue(tdq.NamedParams["18"].Type.Equals(NHibernateUtil.Guid)); tdq.SetParameter(10, 123456m); diff --git a/src/NHibernate.Test/TestCase.cs b/src/NHibernate.Test/TestCase.cs index 4e4507a6caa..cfd31415062 100644 --- a/src/NHibernate.Test/TestCase.cs +++ b/src/NHibernate.Test/TestCase.cs @@ -426,5 +426,14 @@ protected virtual string CacheConcurrencyStrategy } #endregion + + #region Utilities + + protected DateTime RoundForDialect(DateTime value) + { + return AbstractDateTimeType.Round(value, Dialect.TimestampResolutionInTicks); + } + + #endregion } } diff --git a/src/NHibernate.Test/TestDialect.cs b/src/NHibernate.Test/TestDialect.cs index 6d7fe8b4662..f1e73367118 100644 --- a/src/NHibernate.Test/TestDialect.cs +++ b/src/NHibernate.Test/TestDialect.cs @@ -55,6 +55,11 @@ public TestDialect(Dialect.Dialect dialect) /// public virtual bool SupportsEmptyInserts => true; + /// + /// Supports condition not bound to any data, like "where @p1 = @p2". + /// + public virtual bool SupportsNonDataBoundCondition => true; + public bool SupportsSqlType(SqlType sqlType) { try diff --git a/src/NHibernate.Test/TestDialects/FirebirdTestDialect.cs b/src/NHibernate.Test/TestDialects/FirebirdTestDialect.cs index f4eccb87989..96e4181b1cd 100644 --- a/src/NHibernate.Test/TestDialects/FirebirdTestDialect.cs +++ b/src/NHibernate.Test/TestDialects/FirebirdTestDialect.cs @@ -7,5 +7,6 @@ public FirebirdTestDialect(Dialect.Dialect dialect) : base(dialect) } public override bool SupportsComplexExpressionInGroupBy => false; + public override bool SupportsNonDataBoundCondition => false; } } diff --git a/src/NHibernate.Test/TypesTest/AbstractDateTimeTypeFixture.cs b/src/NHibernate.Test/TypesTest/AbstractDateTimeTypeFixture.cs new file mode 100644 index 00000000000..75fc976284e --- /dev/null +++ b/src/NHibernate.Test/TypesTest/AbstractDateTimeTypeFixture.cs @@ -0,0 +1,649 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Reflection; +using NHibernate.Cfg; +using NHibernate.Driver; +using NHibernate.Engine; +using NHibernate.SqlCommand; +using NHibernate.SqlTypes; +using NHibernate.Tool.hbm2ddl; +using NHibernate.Type; +using NHibernate.Util; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + [TestFixture] + public abstract class AbstractDateTimeTypeFixture : TypeFixtureBase + { + protected abstract AbstractDateTimeType Type { get; } + protected virtual bool RevisionCheck => true; + + protected const int DateId = 1; + protected const int AdditionalDateId = 2; + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + + var driverClass = ReflectHelper.ClassForName(configuration.GetProperty(Cfg.Environment.ConnectionDriver)); + ClientDriverWithParamsStats.DriverClass = driverClass; + + configuration.SetProperty( + Cfg.Environment.ConnectionDriver, + typeof(ClientDriverWithParamsStats).AssemblyQualifiedName); + } + + protected override void OnSetUp() + { + base.OnSetUp(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeClass + { + Id = DateId, + Value = Now + }; + s.Save(d); + t.Commit(); + } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from DateTimeClass").ExecuteUpdate(); + t.Commit(); + } + } + + protected override void DropSchema() + { + (Sfi.ConnectionProvider.Driver as ClientDriverWithParamsStats)?.CleanUp(); + base.DropSchema(); + } + + [Test] + public void Next() + { + var current = DateTime.Parse("2004-01-01"); + var next = Type.Next(current, null); + + Assert.That(next, Is.TypeOf(), "next should be DateTime"); + Assert.That(next, Is.GreaterThan(current), "next should be greater than current"); + } + + [Test] + public void Seed() + { + Assert.That(Type.Seed(null), Is.TypeOf(), "seed should be DateTime"); + } + + [Test] + public void DeepCopyNotNull() + { + var value1 = DateTime.Now; + var value2 = Type.DeepCopy(value1, null); + + Assert.That(value2, Is.EqualTo(value1), "Copies should be the same."); + + // Bit moot with structs... + value2 = ((DateTime) value2).AddHours(2); + Assert.That(value2, Is.Not.EqualTo(value1), "value2 was changed, value1 should not have changed also."); + } + + [Test] + [TestCase(DateTimeKind.Unspecified)] + [TestCase(DateTimeKind.Local)] + [TestCase(DateTimeKind.Utc)] + public void Equality(DateTimeKind kind) + { + var testDate = GetTestDate(kind); + var sameDate = GetSameDate(testDate); + Assert.That(Type.IsEqual(testDate, sameDate), Is.True); + } + + [Test] + [TestCase(DateTimeKind.Unspecified)] + [TestCase(DateTimeKind.Local)] + [TestCase(DateTimeKind.Utc)] + public void Inequality(DateTimeKind kind) + { + var testDate = GetTestDate(kind); + var diffDate = GetDifferentDate(testDate); + Assert.That(Type.IsEqual(testDate, diffDate), Is.False); + } + + [Test] + [TestCase(DateTimeKind.Unspecified)] + [TestCase(DateTimeKind.Local)] + [TestCase(DateTimeKind.Utc)] + public void ReadWrite(DateTimeKind kind) + { + var entity = new DateTimeClass + { + Id = AdditionalDateId, + Value = GetTestDate(kind) + }; + + var typeKind = GetTypeKind(); + // Now must be acquired before transaction because some db freezes current_timestamp at transaction start, + // like PostgreSQL. https://www.postgresql.org/docs/7.2/static/functions-datetime.html#AEN6700 + // This then wrecks tests with DbTimestampType if the always out of tran Now is called for fetching + // beforeNow only after transaction start. + // And account db accuracy + var beforeNow = Now.AddTicks(-DateAccuracyInTicks); + // Save + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(entity); + if (kind != typeKind && typeKind != DateTimeKind.Unspecified) + { + Assert.That(() => t.Commit(), Throws.TypeOf()); + return; + } + t.Commit(); + } + var afterNow = Now.AddTicks(DateAccuracyInTicks); + + if (RevisionCheck) + { + Assert.That(entity.Revision, Is.GreaterThan(beforeNow).And.LessThan(afterNow), "Revision not correctly seeded."); + if (typeKind != DateTimeKind.Unspecified) + Assert.That(entity.Revision.Kind, Is.EqualTo(typeKind), "Revision kind not correctly seeded."); + Assert.That(entity.NullableValue, Is.Null, "NullableValue unexpectedly seeded."); + } + + // Retrieve, compare then update + DateTimeClass retrieved; + using (var s = OpenSession()) + { + using (var t = s.BeginTransaction()) + { + retrieved = s.Get(AdditionalDateId); + + Assert.That(retrieved, Is.Not.Null, "Entity not saved or cannot be retrieved by its key."); + Assert.That(retrieved.Value, Is.EqualTo(GetExpectedValue(entity.Value)), "Unexpected value."); + if (RevisionCheck) + Assert.That(retrieved.Revision, Is.EqualTo(entity.Revision), "Revision should be the same."); + Assert.That(retrieved.NullableValue, Is.EqualTo(entity.NullableValue), "NullableValue should be the same."); + if (typeKind != DateTimeKind.Unspecified) + { + Assert.That(retrieved.Value.Kind, Is.EqualTo(typeKind), "Value kind not correctly retrieved."); + if (RevisionCheck) + Assert.That(retrieved.Revision.Kind, Is.EqualTo(typeKind), "Revision kind not correctly retrieved."); + } + t.Commit(); + } + beforeNow = Now.AddTicks(-DateAccuracyInTicks); + using (var t = s.BeginTransaction()) + { + retrieved.NullableValue = GetTestDate(kind); + retrieved.Value = GetTestDate(kind).AddMonths(-1); + t.Commit(); + } + afterNow = Now.AddTicks(DateAccuracyInTicks); + } + + if (RevisionCheck) + { + Assert.That( + retrieved.Revision, + Is.GreaterThan(beforeNow).And.LessThan(afterNow).And.GreaterThanOrEqualTo(entity.Revision), + "Revision not correctly incremented."); + if (typeKind != DateTimeKind.Unspecified) + Assert.That(retrieved.Revision.Kind, Is.EqualTo(typeKind), "Revision kind incorrectly changed."); + } + + // Retrieve and compare again + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrievedAgain = s.Get(AdditionalDateId); + + Assert.That(retrievedAgain, Is.Not.Null, "Entity deleted or cannot be retrieved again by its key."); + Assert.That( + retrievedAgain.Value, + Is.EqualTo(GetExpectedValue(retrieved.Value)), + "Unexpected value at second compare."); + if (RevisionCheck) + Assert.That(retrievedAgain.Revision, Is.EqualTo(retrieved.Revision), "Revision should be the same again."); + Assert.That( + retrievedAgain.NullableValue, + Is.EqualTo(GetExpectedValue(retrieved.NullableValue.Value)), + "Unexpected NullableValue at second compare."); + if (typeKind != DateTimeKind.Unspecified) + { + Assert.That(retrievedAgain.Value.Kind, Is.EqualTo(typeKind), "Value kind not correctly retrieved again."); + if (RevisionCheck) + Assert.That(retrievedAgain.Revision.Kind, Is.EqualTo(typeKind), "Revision kind not correctly retrieved again."); + Assert.That( + retrievedAgain.NullableValue.Value.Kind, + Is.EqualTo(typeKind), + "NullableValue kind not correctly retrieved again."); + } + t.Commit(); + } + } + + [Test] + public void PropertiesHasExpectedType() + { + var classMetaData = Sfi.GetClassMetadata(typeof(DateTimeClass)); + Assert.That( + classMetaData.GetPropertyType(nameof(DateTimeClass.Revision)), + Is.EqualTo(Type)); + Assert.That( + classMetaData.GetPropertyType(nameof(DateTimeClass.Value)), + Is.EqualTo(Type)); + Assert.That( + classMetaData.GetPropertyType(nameof(DateTimeClass.NullableValue)), + Is.EqualTo(Type)); + } + + [Test] + public void DbHasExpectedType() + { + var validator = new SchemaValidator(cfg); + validator.Validate(); + } + + [Test] + public virtual void SaveUseExpectedSqlType() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeClass + { + Id = 2, + Value = Now, + NullableValue = Now + }; + driver.ClearStats(); + s.Save(d); + t.Commit(); + } + + // 2 properties + revision + AssertSqlType(driver, 3, true); + } + + [Test] + public virtual void UpdateUseExpectedSqlType() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = s.Get(DateId); + d.Value = Now; + d.NullableValue = Now; + driver.ClearStats(); + t.Commit(); + } + + // 2 properties + revision x 2 (check + update) + AssertSqlType(driver, 4, true); + } + + [Test] + public virtual void QueryUseExpectedSqlType() + { + if (!TestDialect.SupportsNonDataBoundCondition) + Assert.Ignore("Dialect does not support the test query"); + + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetDateTime("value", Now) + .SetDateTime("nullableValue", Now) + .SetDateTime("revision", Now) + .SetDateTime("other1", Now) + .SetDateTime("other2", Now); + driver.ClearStats(); + q.List(); + t.Commit(); + } + + AssertSqlType(driver, 5, false); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetParameter("value", Now, Type) + .SetParameter("nullableValue", Now, Type) + .SetParameter("revision", Now, Type) + .SetParameter("other1", Now, Type) + .SetParameter("other2", Now, Type); + driver.ClearStats(); + q.List(); + t.Commit(); + } + + AssertSqlType(driver, 5, true); + } + + /// + /// Tests if the type FromStringValue implementation behaves as expected. + /// + /// + [Test] + [TestCase("2011-01-27T15:50:59.6220000+02:00")] + [TestCase("2011-01-27T14:50:59.6220000+01:00")] + [TestCase("2011-01-27T13:50:59.6220000Z")] + public void FromStringValue_ParseValidValues(string timestampValue) + { + var timestamp = DateTime.Parse(timestampValue); + + Assert.That( + timestamp.Kind, + Is.EqualTo(DateTimeKind.Local), + "Kind is NOT Local. dotnet framework parses datetime values with kind set to Local and " + + "time correct to local timezone."); + + var typeKind = GetTypeKind(); + if (typeKind == DateTimeKind.Utc) + timestamp = timestamp.ToUniversalTime(); + + var value = (DateTime) Type.FromStringValue(timestampValue); + + Assert.That(value, Is.EqualTo(timestamp), timestampValue); + + if (typeKind != DateTimeKind.Unspecified) + Assert.AreEqual(GetTypeKind(), value.Kind, "Unexpected FromStringValue kind"); + } + + /// + /// Test the framework IsEqual behavior. If the test fails then the + /// and implemention could not work propertly at run-time. + /// + [Test, Category("Expected framework behavior")] + public void ExpectedIsEqualDotnetFrameworkBehavior() + { + const string assertMessage = "Values should be equal dotnet framework ignores Kind value."; + var utc = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Utc); + var local = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Local); + var unspecified = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Unspecified); + Assert.That(utc, Is.EqualTo(local), assertMessage); + Assert.That(utc, Is.EqualTo(unspecified), assertMessage); + Assert.That(unspecified, Is.EqualTo(local), assertMessage); + } + + private void AssertSqlType(ClientDriverWithParamsStats driver, int expectedCount, bool exactType) + { + var typeSqlTypes = Type.SqlTypes(Sfi); + if (typeSqlTypes.Any(t => t is DateTime2SqlType)) + { + var expectedType = exactType ? typeSqlTypes.First(t => t is DateTime2SqlType) : SqlTypeFactory.DateTime2; + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.DateTime2 usage count."); + Assert.That(driver.GetCount(DbType.DateTime), Is.EqualTo(0), "Found unexpected DbType.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected DbType.DateTime2 usage count."); + } + else if (typeSqlTypes.Any(t => t is DateTimeSqlType)) + { + var expectedType = exactType ? typeSqlTypes.First(t => t is DateTimeSqlType) : SqlTypeFactory.DateTime; + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime2), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime2 usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.DateTime usage count."); + Assert.That(driver.GetCount(DbType.DateTime2), Is.EqualTo(0), "Found unexpected DbType.DateTime2 usages."); + Assert.That(driver.GetCount(expectedType), Is.EqualTo(expectedCount), "Unexpected DbType.DateTime usage count."); + } + else if (typeSqlTypes.Any(t => Equals(t, SqlTypeFactory.Date))) + { + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime usages."); + Assert.That( + driver.GetCount(SqlTypeFactory.Date), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.Date usage count."); + Assert.That(driver.GetCount(DbType.DateTime), Is.EqualTo(0), "Found unexpected DbType.DateTime usages."); + Assert.That(driver.GetCount(DbType.Date), Is.EqualTo(expectedCount), "Unexpected DbType.Date usage count."); + } + else + { + Assert.Ignore("Test does not involve tested types"); + } + } + + protected virtual long DateAccuracyInTicks => Dialect.TimestampResolutionInTicks; + + protected virtual DateTime Now => GetTypeKind() == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.Now; + + protected virtual DateTime GetTestDate(DateTimeKind kind) + { + return AbstractDateTimeType.Round( + kind == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.SpecifyKind(DateTime.Now, kind), + DateAccuracyInTicks) + // Take another date than now for checking the value do not get overridden by seeding. + .AddDays(1); + } + + private DateTime GetExpectedValue(DateTime value) + { + var expectedValue = value; + var typeKind = GetTypeKind(); + if (typeKind != DateTimeKind.Unspecified && typeKind != value.Kind && value.Kind != DateTimeKind.Unspecified) + { + expectedValue = typeKind == DateTimeKind.Local ? expectedValue.ToLocalTime() : expectedValue.ToUniversalTime(); + } + return expectedValue; + } + + /// + /// Return a date time still considered equal but as different as possible. + /// + /// The originale date time. + /// An equal date time. + protected virtual DateTime GetSameDate(DateTime original) + { + if (GetTypeKind() != DateTimeKind.Unspecified) + return new DateTime(original.Ticks, original.Kind); + + switch (original.Kind) + { + case DateTimeKind.Local: + return DateTime.SpecifyKind(original, DateTimeKind.Unspecified); + case DateTimeKind.Unspecified: + return DateTime.SpecifyKind(original, DateTimeKind.Utc); + default: + return DateTime.SpecifyKind(original, DateTimeKind.Local); + } + } + + /// + /// Return a different date time but as few different as possible. + /// + /// The originale date time. + /// An inequal date time. + protected virtual DateTime GetDifferentDate(DateTime original) + { + return original.AddTicks(DateAccuracyInTicks); + } + + private static readonly PropertyInfo _kindProperty = + typeof(AbstractDateTimeType).GetProperty("Kind", BindingFlags.Instance | BindingFlags.NonPublic); + + protected DateTimeKind GetTypeKind() + { + return (DateTimeKind) _kindProperty.GetValue(Type); + } + } + + public class ClientDriverWithParamsStats : IDriver + { + private readonly Dictionary _usedSqlTypes = new Dictionary(); + private readonly Dictionary _usedDbTypes = new Dictionary(); + internal static System.Type DriverClass { get; set; } + + private readonly IDriver _driverImplementation; + + public ClientDriverWithParamsStats() + { + _driverImplementation = (IDriver) Cfg.Environment.BytecodeProvider.ObjectsFactory.CreateInstance(DriverClass); + } + + private static void Inc(T type, IDictionary dic) + { + dic[type] = GetCount(type, dic) + 1; + } + + private static int GetCount(T type, IDictionary dic) => + dic.TryGetValue(type, out var count) ? count : 0; + + public void ClearStats() + { + _usedSqlTypes.Clear(); + _usedDbTypes.Clear(); + } + + public int GetCount(SqlType type) => + GetCount(type, _usedSqlTypes); + + public int GetCount(DbType type) => + GetCount(type, _usedDbTypes); + + DbCommand IDriver.GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes) + { + var cmd = _driverImplementation.GenerateCommand(type, sqlString, parameterTypes); + + foreach (var sqlType in parameterTypes) + { + Inc(sqlType, _usedSqlTypes); + } + foreach (DbParameter param in cmd.Parameters) + { + Inc(param.DbType, _usedDbTypes); + } + + return cmd; + } + + DbParameter IDriver.GenerateParameter(DbCommand command, string name, SqlType sqlType) + { + var param = _driverImplementation.GenerateParameter(command, name, sqlType); + Inc(sqlType, _usedSqlTypes); + Inc(param.DbType, _usedDbTypes); + return param; + } + + #region Firebird mess + + public void CleanUp() + { + if (_driverImplementation is FirebirdClientDriver fbDriver) + { + // Firebird will pool each connection created during the test and will marked as used any table + // referenced by queries. It will at best delays those tables drop until connections are actually + // closed, or immediately fail dropping them. + // This results in other tests failing when they try to create tables with same name. + // By clearing the connection pool the tables will get dropped. This is done by the following code. + // Moved from NH1908 test case, contributed by Amro El-Fakharany. + fbDriver.ClearPool(null); + } + } + + #endregion + + #region Pure forwarding + + void IDriver.Configure(IDictionary settings) + { + _driverImplementation.Configure(settings); + } + + DbConnection IDriver.CreateConnection() + { + return _driverImplementation.CreateConnection(); + } + + bool IDriver.SupportsMultipleOpenReaders => _driverImplementation.SupportsMultipleOpenReaders; + + void IDriver.PrepareCommand(DbCommand command) + { + _driverImplementation.PrepareCommand(command); + } + + void IDriver.RemoveUnusedCommandParameters(DbCommand cmd, SqlString sqlString) + { + _driverImplementation.RemoveUnusedCommandParameters(cmd, sqlString); + } + + void IDriver.ExpandQueryParameters(DbCommand cmd, SqlString sqlString, SqlType[] parameterTypes) + { + _driverImplementation.ExpandQueryParameters(cmd, sqlString, parameterTypes); + } + + IResultSetsCommand IDriver.GetResultSetsCommand(ISessionImplementor session) + { + return _driverImplementation.GetResultSetsCommand(session); + } + + bool IDriver.SupportsMultipleQueries => _driverImplementation.SupportsMultipleQueries; + + void IDriver.AdjustCommand(DbCommand command) + { + _driverImplementation.AdjustCommand(command); + } + + bool IDriver.RequiresTimeSpanForTime => _driverImplementation.RequiresTimeSpanForTime; + + bool IDriver.SupportsSystemTransactions => _driverImplementation.SupportsSystemTransactions; + + bool IDriver.SupportsNullEnlistment => _driverImplementation.SupportsNullEnlistment; + + bool IDriver.SupportsEnlistmentWhenAutoEnlistmentIsDisabled => + _driverImplementation.SupportsEnlistmentWhenAutoEnlistmentIsDisabled; + + bool IDriver.HasDelayedDistributedTransactionCompletion => + _driverImplementation.HasDelayedDistributedTransactionCompletion; + + DateTime IDriver.MinDate => _driverImplementation.MinDate; + + #endregion + } +} diff --git a/src/NHibernate.Test/TypesTest/DateClass.cs b/src/NHibernate.Test/TypesTest/DateClass.cs deleted file mode 100644 index 4803145e152..00000000000 --- a/src/NHibernate.Test/TypesTest/DateClass.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace NHibernate.Test.TypesTest -{ - public class DateClass - { - public DateTime? DateValue { get; set; } - } -} \ No newline at end of file diff --git a/src/NHibernate.Test/TypesTest/DateClass.hbm.xml b/src/NHibernate.Test/TypesTest/DateClass.hbm.xml index 8cc5d4daa59..baf1e7d32c0 100644 --- a/src/NHibernate.Test/TypesTest/DateClass.hbm.xml +++ b/src/NHibernate.Test/TypesTest/DateClass.hbm.xml @@ -1,17 +1,14 @@ - - + - - - + + + + - - - 1900/01/01 - - + + + - \ No newline at end of file + diff --git a/src/NHibernate.Test/TypesTest/DateTime2Class.hbm.xml b/src/NHibernate.Test/TypesTest/DateTime2Class.hbm.xml new file mode 100644 index 00000000000..60c8261f943 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTime2Class.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DateTime2TypeFixture.cs b/src/NHibernate.Test/TypesTest/DateTime2TypeFixture.cs index 250ace18a62..5eec555af67 100644 --- a/src/NHibernate.Test/TypesTest/DateTime2TypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/DateTime2TypeFixture.cs @@ -1,4 +1,6 @@ using System; +using NHibernate.Driver; +using NHibernate.SqlTypes; using NHibernate.Type; using NUnit.Framework; @@ -8,48 +10,44 @@ namespace NHibernate.Test.TypesTest /// TestFixtures for the . /// [TestFixture] - public class DateTime2TypeFixture + [Obsolete] + public class DateTime2TypeFixture : AbstractDateTimeTypeFixture { - [Test] - public void Next() - { - DateTimeType type = NHibernateUtil.DateTime2; - object current = DateTime.Now.AddMilliseconds(-1); - object next = type.Next(current, null); + protected override bool AppliesTo(Dialect.Dialect dialect) => + TestDialect.SupportsSqlType(SqlTypeFactory.DateTime2); - Assert.That(next, Is.TypeOf().And.GreaterThan(current)); - } + protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) => + // Cannot handle DbType.DateTime2 via .Net ODBC. + !(factory.ConnectionProvider.Driver is OdbcDriver); - [Test] - public void Seed() - { - DateTimeType type = NHibernateUtil.DateTime; - Assert.IsTrue(type.Seed(null) is DateTime, "seed should be DateTime"); - } + protected override string TypeName => "DateTime2"; + protected override AbstractDateTimeType Type => NHibernateUtil.DateTime2; [Test] - public void DeepCopyNotNull() + public void ObsoleteMessage() { - NullableType type = NHibernateUtil.DateTime; - - object value1 = DateTime.Now; - object value2 = type.DeepCopy(value1, null); - - Assert.AreEqual(value1, value2, "Copies should be the same."); + using (var spy = new LogSpy(typeof(TypeFactory))) + { + TypeFactory.Basic(NHibernateUtil.DateTime2.Name); + Assert.That( + spy.GetWholeLog(), + Does.Contain($"{NHibernateUtil.DateTime2.Name} is obsolete. Use DateTimeType instead").IgnoreCase); + } + } + } + [TestFixture] + [Obsolete] + public class DateTime2TypeWithScaleFixture : DateTimeTypeWithScaleFixture + { + protected override bool AppliesTo(Dialect.Dialect dialect) => + TestDialect.SupportsSqlType(SqlTypeFactory.DateTime2); - value2 = ((DateTime)value2).AddHours(2); - Assert.IsFalse(value1 == value2, "value2 was changed, value1 should not have changed also."); - } + protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) => + // Cannot handle DbType.DateTime2 via .Net ODBC. + !(factory.ConnectionProvider.Driver is OdbcDriver); - [Test] - public void EqualityShouldIgnoreKindAndNotIgnoreMillisecond() - { - var type = NHibernateUtil.DateTime; - var localTime = DateTime.Now; - var unspecifiedKid = new DateTime(localTime.Ticks, DateTimeKind.Unspecified); - Assert.That(type.IsEqual(localTime, unspecifiedKid), Is.True); - Assert.That(type.IsEqual(localTime, unspecifiedKid), Is.True); - } + protected override string TypeName => "DateTime2WithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetDateTime2Type(3); } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/TypesTest/DateTime2WithScaleClass.hbm.xml b/src/NHibernate.Test/TypesTest/DateTime2WithScaleClass.hbm.xml new file mode 100644 index 00000000000..7f9ad6ee1e6 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTime2WithScaleClass.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DateTimeClass.cs b/src/NHibernate.Test/TypesTest/DateTimeClass.cs index 48cb1ae4862..86c26cab2b3 100644 --- a/src/NHibernate.Test/TypesTest/DateTimeClass.cs +++ b/src/NHibernate.Test/TypesTest/DateTimeClass.cs @@ -2,37 +2,11 @@ namespace NHibernate.Test.TypesTest { - /// - /// Summary description for GuidClass. - /// public class DateTimeClass { - private int _id; - private DateTime? _utcDateTimeValue; - private DateTime? _localDateTimeValue; - public DateTimeClass() - { - NormalDateTimeValue = DateTime.Today; - } - - public int Id - { - get { return _id; } - set { _id = value; } - } - - public DateTime? UtcDateTimeValue - { - get { return _utcDateTimeValue; } - set { _utcDateTimeValue = value; } - } - - public DateTime? LocalDateTimeValue - { - get { return _localDateTimeValue; } - set { _localDateTimeValue = value; } - } - - public DateTime NormalDateTimeValue { get; set; } + public int Id { get; set; } + public DateTime Value { get; set; } + public DateTime Revision { get; protected set; } + public DateTime? NullableValue { get; set; } } } diff --git a/src/NHibernate.Test/TypesTest/DateTimeClass.hbm.xml b/src/NHibernate.Test/TypesTest/DateTimeClass.hbm.xml index e61b06f99d0..9ad6f883750 100644 --- a/src/NHibernate.Test/TypesTest/DateTimeClass.hbm.xml +++ b/src/NHibernate.Test/TypesTest/DateTimeClass.hbm.xml @@ -1,18 +1,14 @@ - + - - - - - - - - - - - + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DateTimeNoMsClass.hbm.xml b/src/NHibernate.Test/TypesTest/DateTimeNoMsClass.hbm.xml new file mode 100644 index 00000000000..f530da5e973 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTimeNoMsClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DateTimeOffsetClass.cs b/src/NHibernate.Test/TypesTest/DateTimeOffsetClass.cs new file mode 100644 index 00000000000..37d2a2b1188 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTimeOffsetClass.cs @@ -0,0 +1,12 @@ +using System; + +namespace NHibernate.Test.TypesTest +{ + public class DateTimeOffsetClass + { + public int Id { get; set; } + public DateTimeOffset Value { get; set; } + public DateTimeOffset Revision { get; protected set; } + public DateTimeOffset? NullableValue { get; set; } + } +} diff --git a/src/NHibernate.Test/TypesTest/DateTimeOffsetClass.hbm.xml b/src/NHibernate.Test/TypesTest/DateTimeOffsetClass.hbm.xml new file mode 100644 index 00000000000..19832645b94 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTimeOffsetClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DateTimeOffsetTypeFixture.cs b/src/NHibernate.Test/TypesTest/DateTimeOffsetTypeFixture.cs new file mode 100644 index 00000000000..0881a08e878 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTimeOffsetTypeFixture.cs @@ -0,0 +1,415 @@ +using System; +using System.Data; +using System.Linq; +using NHibernate.Cfg; +using NHibernate.Driver; +using NHibernate.SqlTypes; +using NHibernate.Tool.hbm2ddl; +using NHibernate.Type; +using NHibernate.Util; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + [TestFixture] + public class DateTimeOffsetTypeFixture : TypeFixtureBase + { + protected override string TypeName => "DateTimeOffset"; + protected virtual DateTimeOffsetType Type => NHibernateUtil.DateTimeOffset; + protected virtual bool RevisionCheck => true; + + protected const int DateId = 1; + protected const int AdditionalDateId = 2; + + protected override bool AppliesTo(Dialect.Dialect dialect) => + TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet); + + protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) => + // Cannot handle DbType.DateTimeOffset via .Net ODBC. + !(factory.ConnectionProvider.Driver is OdbcDriver); + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + + var driverClass = ReflectHelper.ClassForName(configuration.GetProperty(Cfg.Environment.ConnectionDriver)); + ClientDriverWithParamsStats.DriverClass = driverClass; + + configuration.SetProperty( + Cfg.Environment.ConnectionDriver, + typeof(ClientDriverWithParamsStats).AssemblyQualifiedName); + } + + protected override void OnSetUp() + { + base.OnSetUp(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeOffsetClass + { + Id = DateId, + Value = Now + }; + s.Save(d); + t.Commit(); + } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from DateTimeOffsetClass").ExecuteUpdate(); + t.Commit(); + } + } + + protected override void DropSchema() + { + (Sfi.ConnectionProvider.Driver as ClientDriverWithParamsStats)?.CleanUp(); + base.DropSchema(); + } + + [Test] + public void Next() + { + var current = DateTimeOffset.Parse("2004-01-01"); + var next = Type.Next(current, null); + + Assert.That(next, Is.TypeOf(), "next should be DateTimeOffset"); + Assert.That(next, Is.GreaterThan(current), "next should be greater than current"); + } + + [Test] + public void Seed() + { + Assert.That(Type.Seed(null), Is.TypeOf(), "seed should be DateTime"); + } + + [Test] + public void DeepCopyNotNull() + { + var value1 = DateTimeOffset.Now; + var value2 = Type.DeepCopy(value1, null); + + Assert.That(value2, Is.EqualTo(value1), "Copies should be the same."); + } + + [Test] + public void Equality() + { + var testDate = GetTestDate(); + var sameDate = GetSameDate(testDate); + Assert.That(Type.IsEqual(testDate, sameDate), Is.True); + } + + [Test] + public void Inequality() + { + var testDate = GetTestDate(); + var diffDate = GetDifferentDate(testDate); + Assert.That(Type.IsEqual(testDate, diffDate), Is.False); + } + + [Test] + public void ReadWrite() + { + var entity = new DateTimeOffsetClass + { + Id = AdditionalDateId, + Value = GetTestDate() + }; + + // Now must be acquired before transaction because some db freezes current_timestamp at transaction start, + // like PostgreSQL. https://www.postgresql.org/docs/7.2/static/functions-datetime.html#AEN6700 + // This then wrecks tests with DbTimestampType if the always out of tran Now is called for fetching + // beforeNow only after transaction start. + // And account db accuracy + var beforeNow = Now.AddTicks(-DateAccuracyInTicks); + // Save + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(entity); + t.Commit(); + } + var afterNow = Now.AddTicks(DateAccuracyInTicks); + + if (RevisionCheck) + { + Assert.That(entity.Revision, Is.GreaterThan(beforeNow).And.LessThan(afterNow), "Revision not correctly seeded."); + Assert.That(entity.NullableValue, Is.Null, "NullableValue unexpectedly seeded."); + } + + // Retrieve, compare then update + DateTimeOffsetClass retrieved; + using (var s = OpenSession()) + { + using (var t = s.BeginTransaction()) + { + retrieved = s.Get(AdditionalDateId); + + Assert.That(retrieved, Is.Not.Null, "Entity not saved or cannot be retrieved by its key."); + Assert.That(retrieved.Value, Is.EqualTo(entity.Value), "Unexpected value."); + if (RevisionCheck) + Assert.That(retrieved.Revision, Is.EqualTo(entity.Revision), "Revision should be the same."); + Assert.That(retrieved.NullableValue, Is.EqualTo(entity.NullableValue), "NullableValue should be the same."); + t.Commit(); + } + beforeNow = Now.AddTicks(-DateAccuracyInTicks); + using (var t = s.BeginTransaction()) + { + retrieved.NullableValue = GetTestDate(); + retrieved.Value = GetTestDate().AddMonths(-1); + t.Commit(); + } + afterNow = Now.AddTicks(DateAccuracyInTicks); + } + + if (RevisionCheck) + { + Assert.That( + retrieved.Revision, + Is.GreaterThan(beforeNow).And.LessThan(afterNow).And.GreaterThanOrEqualTo(entity.Revision), + "Revision not correctly incremented."); + } + + // Retrieve and compare again + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrievedAgain = s.Get(AdditionalDateId); + + Assert.That(retrievedAgain, Is.Not.Null, "Entity deleted or cannot be retrieved again by its key."); + Assert.That( + retrievedAgain.Value, + Is.EqualTo(retrieved.Value), + "Unexpected value at second compare."); + if (RevisionCheck) + Assert.That(retrievedAgain.Revision, Is.EqualTo(retrieved.Revision), "Revision should be the same again."); + Assert.That( + retrievedAgain.NullableValue, + Is.EqualTo(retrieved.NullableValue.Value), + "Unexpected NullableValue at second compare."); + t.Commit(); + } + } + + [Test] + public void PropertiesHasExpectedType() + { + var classMetaData = Sfi.GetClassMetadata(typeof(DateTimeOffsetClass)); + Assert.That( + classMetaData.GetPropertyType(nameof(DateTimeOffsetClass.Revision)), + Is.EqualTo(Type)); + Assert.That( + classMetaData.GetPropertyType(nameof(DateTimeOffsetClass.Value)), + Is.EqualTo(Type)); + Assert.That( + classMetaData.GetPropertyType(nameof(DateTimeOffsetClass.NullableValue)), + Is.EqualTo(Type)); + } + + [Test] + public void DbHasExpectedType() + { + var validator = new SchemaValidator(cfg); + validator.Validate(); + } + + [Test] + public virtual void SaveUseExpectedSqlType() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = new DateTimeOffsetClass + { + Id = 2, + Value = Now, + NullableValue = Now + }; + driver.ClearStats(); + s.Save(d); + t.Commit(); + } + + // 2 properties + revision + AssertSqlType(driver, 3, true); + } + + [Test] + public virtual void UpdateUseExpectedSqlType() + { + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var d = s.Get(DateId); + d.Value = Now; + d.NullableValue = Now; + driver.ClearStats(); + t.Commit(); + } + + // 2 properties + revision x 2 (check + update) + AssertSqlType(driver, 4, true); + } + + [Test] + public virtual void QueryUseExpectedSqlType() + { + if (!TestDialect.SupportsNonDataBoundCondition) + Assert.Ignore("Dialect does not support the test query"); + + var driver = (ClientDriverWithParamsStats) Sfi.ConnectionProvider.Driver; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeOffsetClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetDateTimeOffset("value", Now) + .SetDateTimeOffset("nullableValue", Now) + .SetDateTimeOffset("revision", Now) + .SetDateTimeOffset("other1", Now) + .SetDateTimeOffset("other2", Now); + driver.ClearStats(); + q.List(); + t.Commit(); + } + + AssertSqlType(driver, 5, false); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var q = s + .CreateQuery( + "from DateTimeOffsetClass d where d.Value = :value and " + + "d.NullableValue = :nullableValue and " + + "d.Revision = :revision and " + + ":other1 = :other2") + .SetParameter("value", Now, Type) + .SetParameter("nullableValue", Now, Type) + .SetParameter("revision", Now, Type) + .SetParameter("other1", Now, Type) + .SetParameter("other2", Now, Type); + driver.ClearStats(); + q.List(); + t.Commit(); + } + + AssertSqlType(driver, 5, true); + } + + private void AssertSqlType(ClientDriverWithParamsStats driver, int expectedCount, bool exactType) + { + var typeSqlTypes = Type.SqlTypes(Sfi); + if (typeSqlTypes.Any(t => t is DateTimeOffsetSqlType)) + { + var expectedType = exactType ? typeSqlTypes.First(t => t is DateTimeOffsetSqlType) : SqlTypeFactory.DateTimeOffSet; + Assert.That( + driver.GetCount(SqlTypeFactory.DateTime), + Is.EqualTo(0), + "Found unexpected SqlTypeFactory.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected SqlTypeFactory.DateTime2 usage count."); + Assert.That(driver.GetCount(DbType.DateTime), Is.EqualTo(0), "Found unexpected DbType.DateTime usages."); + Assert.That( + driver.GetCount(expectedType), + Is.EqualTo(expectedCount), + "Unexpected DbType.DateTime2 usage count."); + } + else + { + Assert.Ignore("Test does not involve tested types"); + } + } + + protected virtual long DateAccuracyInTicks => Dialect.TimestampResolutionInTicks; + + protected virtual DateTimeOffset Now => DateTimeOffset.Now; + + protected virtual DateTimeOffset GetTestDate() + { + return DateTimeOffsetType.Round(Now, DateAccuracyInTicks) + // Take another date than now for checking the value do not get overridden by seeding. + .AddDays(1); + } + + /// + /// Return a date time still considered equal but as different as possible. + /// + /// The originale date time. + /// An equal date time. + protected virtual DateTimeOffset GetSameDate(DateTimeOffset original) + { + return new DateTimeOffset(original.Ticks, original.Offset); + } + + /// + /// Return a different date time but as few different as possible. + /// + /// The originale date time. + /// An inequal date time. + protected virtual DateTimeOffset GetDifferentDate(DateTimeOffset original) + { + return original.AddTicks(DateAccuracyInTicks); + } + } + + [TestFixture] + public class DateTimeOffsetTypeWithScaleFixture : DateTimeOffsetTypeFixture + { + protected override string TypeName => "DateTimeOffsetWithScale"; + protected override DateTimeOffsetType Type => (DateTimeOffsetType)TypeFactory.GetDateTimeOffsetType(3); + protected override long DateAccuracyInTicks => Math.Max(TimeSpan.TicksPerMillisecond, base.DateAccuracyInTicks); + // The timestamp rounding in seeding does not account scale. + protected override bool RevisionCheck => false; + + [Test] + public void LowerDigitsAreIgnored() + { + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseDate = new DateTimeOffset(2017, 10, 01, 17, 55, 24, 548, TimeSpan.FromHours(2)); + var entity = new DateTimeOffsetClass + { + Id = AdditionalDateId, + Value = baseDate.AddTicks(TimeSpan.TicksPerMillisecond / 3) + }; + Assert.That(entity.Value, Is.Not.EqualTo(baseDate)); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(entity); + t.Commit(); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = s.Load(AdditionalDateId); + Assert.That(retrieved.Value, Is.EqualTo(baseDate)); + t.Commit(); + } + } + } +} diff --git a/src/NHibernate.Test/TypesTest/DateTimeOffsetWithScaleClass.hbm.xml b/src/NHibernate.Test/TypesTest/DateTimeOffsetWithScaleClass.hbm.xml new file mode 100644 index 00000000000..f765812f984 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTimeOffsetWithScaleClass.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DateTimeTypeFixture.cs b/src/NHibernate.Test/TypesTest/DateTimeTypeFixture.cs index bb8f3de44bc..5c7b5ebdfe4 100644 --- a/src/NHibernate.Test/TypesTest/DateTimeTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/DateTimeTypeFixture.cs @@ -1,6 +1,9 @@ using System; +using NHibernate.Cfg; +using NHibernate.Dialect; using NHibernate.Type; using NUnit.Framework; +using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Test.TypesTest { @@ -8,50 +11,125 @@ namespace NHibernate.Test.TypesTest /// TestFixtures for the . /// [TestFixture] - public class DateTimeTypeFixture + public class DateTimeTypeFixture : AbstractDateTimeTypeFixture { + protected override string TypeName => "DateTime"; + protected override AbstractDateTimeType Type => NHibernateUtil.DateTime; + } + + /// + /// TestFixtures for the . + /// + [TestFixture] + public class DateTimeTypeWithScaleFixture : AbstractDateTimeTypeFixture + { + protected override string TypeName => "DateTimeWithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetDateTimeType(3); + protected override long DateAccuracyInTicks => Math.Max(TimeSpan.TicksPerMillisecond, base.DateAccuracyInTicks); + // The timestamp rounding in seeding does not account scale. + protected override bool RevisionCheck => false; + [Test] - public void Next() + public void LowerDigitsAreIgnored() { - DateTimeType type = (DateTimeType) NHibernateUtil.DateTime; - object current = DateTime.Parse("2004-01-01"); - object next = type.Next(current, null); + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseDate = new DateTime(2017, 10, 01, 17, 55, 24, 548, GetTypeKind()); + var entity = new DateTimeClass + { + Id = AdditionalDateId, + Value = baseDate.AddTicks(TimeSpan.TicksPerMillisecond / 3) + }; + Assert.That(entity.Value, Is.Not.EqualTo(baseDate)); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(entity); + t.Commit(); + } - Assert.IsTrue(next is DateTime, "Next should be DateTime"); - Assert.IsTrue((DateTime) next > (DateTime) current, - "next should be greater than current (could be equal depending on how quickly this occurs)"); + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = s.Load(AdditionalDateId); + Assert.That(retrieved.Value, Is.EqualTo(baseDate)); + t.Commit(); + } } + } - [Test] - public void Seed() + // Testing SQL Server 2008 with datetime in db instead of datetime2 + [TestFixture] + public class DateTimeTypeMixedDateTimeFixture : DateTimeTypeFixture + { + protected override bool AppliesTo(Dialect.Dialect dialect) { - DateTimeType type = (DateTimeType) NHibernateUtil.DateTime; - Assert.IsTrue(type.Seed(null) is DateTime, "seed should be DateTime"); + return base.AppliesTo(dialect) && dialect is MsSql2008Dialect; } - [Test] - public void DeepCopyNotNull() + protected override void Configure(Configuration configuration) { - NullableType type = NHibernateUtil.DateTime; + base.Configure(configuration); - object value1 = DateTime.Now; - object value2 = type.DeepCopy(value1, null); + configuration.SetProperty(Environment.SqlTypesKeepDateTime, "true"); + } - Assert.AreEqual(value1, value2, "Copies should be the same."); + /* Uncomment for testing how it fails if NHibernate uses DateTime2 while the db column is DateTime. + * It fails two thirds of times on update with an undue optimistic locking exception, due to the datetimes + * ending with 3.333ms (transmitted as 3) or 6.666ms (transmitted as 7), which does not match the datetime2 + * sent by NHibernate for checking the revision has not changed. Demonstrated by UpdateUseExpectedSqlType. + * It furthermore causes undue optimistic failures if re-updating the same entity without loading it in-between, + * as demonstrated by ReadWrite. + * Another way to demonstrate this is to use a + * mapping, without using SqlTypesKeepDateTime. This causes the column to be typed datetime in db but the + * parameters are still transmitted as datetime2 because this sql-type attribute is only used in DDL. + * + protected override DebugSessionFactory BuildSessionFactory() + { + cfg.SetProperty(Environment.SqlTypesKeepDateTime, "false"); + try + { + return base.BuildSessionFactory(); + } + finally + { + cfg.SetProperty(Environment.SqlTypesKeepDateTime, "true"); + } + } + //*/ + } + /// + /// TestFixtures for the . + /// + [TestFixture] + public class DateTimeNoMsTypeFixture : AbstractDateTimeTypeFixture + { + protected override string TypeName => "DateTimeNoMs"; + protected override AbstractDateTimeType Type => NHibernateUtil.DateTimeNoMs; + protected override bool RevisionCheck => false; + protected override long DateAccuracyInTicks => TimeSpan.TicksPerSecond; - value2 = ((DateTime)value2).AddHours(2); - Assert.IsFalse(value1 == value2, "value2 was changed, value1 should not have changed also."); + protected override DateTime GetTestDate(DateTimeKind kind) + { + var date = base.GetTestDate(kind); + return new DateTime( + date.Year, + date.Month, + date.Day, + date.Hour, + date.Minute, + date.Second, + 0, + kind); } - [Test] - public void EqualityShouldIgnoreKindAndMillisecond() + protected override DateTime GetSameDate(DateTime original) { - var type = (DateTimeType)NHibernateUtil.DateTime; - var localTime = DateTime.Now; - var unspecifiedKid = new DateTime(localTime.Year, localTime.Month, localTime.Day, localTime.Hour, localTime.Minute, localTime.Second, 0, DateTimeKind.Unspecified); - Assert.That(type.IsEqual(localTime, unspecifiedKid), Is.True); - Assert.That(type.IsEqual(localTime, unspecifiedKid), Is.True); + var date = base.GetSameDate(original); + return date.AddMilliseconds(date.Millisecond < 500 ? 500 : -500); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/TypesTest/DateTimeWithScaleClass.hbm.xml b/src/NHibernate.Test/TypesTest/DateTimeWithScaleClass.hbm.xml new file mode 100644 index 00000000000..eca45e8df82 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DateTimeWithScaleClass.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DateTypeTest.cs b/src/NHibernate.Test/TypesTest/DateTypeTest.cs index 4fb566e2f18..7b58f0c71f7 100644 --- a/src/NHibernate.Test/TypesTest/DateTypeTest.cs +++ b/src/NHibernate.Test/TypesTest/DateTypeTest.cs @@ -33,11 +33,20 @@ public void WhenSetParameterNullThenNotThrow() } [TestFixture] - public class DateTypeFixture : TypeFixtureBase + public class DateTypeFixture : AbstractDateTimeTypeFixture { - protected override string TypeName + protected override string TypeName => "Date"; + protected override AbstractDateTimeType Type => NHibernateUtil.Date; + protected override bool RevisionCheck => false; + protected override long DateAccuracyInTicks => TimeSpan.TicksPerDay; + + protected override DateTime GetTestDate(DateTimeKind kind) + => base.GetTestDate(kind).Date; + + protected override DateTime GetSameDate(DateTime original) { - get { return "Date"; } + var date = base.GetSameDate(original); + return date.AddHours(date.Hour < 12 ? 12 : -12); } [Test] @@ -47,7 +56,7 @@ public void ShouldBeDateType() { Assert.Ignore("This test does not apply to " + Dialect); } - var sqlType = Dialect.GetTypeName(NHibernateUtil.Date.SqlType); + var sqlType = Dialect.GetTypeName(Type.SqlType); Assert.That(sqlType.ToLowerInvariant(), Is.EqualTo("date")); } @@ -81,20 +90,36 @@ public void ReadWriteYear750() private void ReadWrite(DateTime expected) { // Add an hour to check it is correctly ignored once read back from db. - var basic = new DateClass { DateValue = expected.AddHours(1) }; - object savedId; + var basic = new DateTimeClass { Id = AdditionalDateId, Value = expected.AddHours(1) }; using (var s = OpenSession()) { - savedId = s.Save(basic); + s.Save(basic); s.Flush(); } using (var s = OpenSession()) { - basic = s.Get(savedId); - Assert.That(basic.DateValue, Is.EqualTo(expected)); + basic = s.Get(AdditionalDateId); + Assert.That(basic.Value, Is.EqualTo(expected)); s.Delete(basic); s.Flush(); } } + + // They furthermore cause some issues with Sql Server Ce, which switch DbType.Date for DbType.DateTime + // on its parameters. + [Ignore("Test relevant for datetime, not for date.")] + public override void SaveUseExpectedSqlType() + { + } + + [Ignore("Test relevant for datetime, not for date.")] + public override void UpdateUseExpectedSqlType() + { + } + + [Ignore("Test relevant for datetime, not for date.")] + public override void QueryUseExpectedSqlType() + { + } } } diff --git a/src/NHibernate.Test/TypesTest/DbTimestampClass.hbm.xml b/src/NHibernate.Test/TypesTest/DbTimestampClass.hbm.xml new file mode 100644 index 00000000000..d678bf9b978 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DbTimestampClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/DbTimestampTypeFixture.cs b/src/NHibernate.Test/TypesTest/DbTimestampTypeFixture.cs new file mode 100644 index 00000000000..368983097b6 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/DbTimestampTypeFixture.cs @@ -0,0 +1,28 @@ +using System; +using NHibernate.Type; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + [TestFixture] + public class DbTimestampTypeFixture : AbstractDateTimeTypeFixture + { + protected override string TypeName => "DbTimestamp"; + protected override AbstractDateTimeType Type => NHibernateUtil.DbTimestamp; + protected override DateTime Now => (DateTime)Type.Seed(_session?.GetSessionImplementation()); + private ISession _session; + + protected override void OnSetUp() + { + _session = OpenSession(); + base.OnSetUp(); + } + + protected override void OnTearDown() + { + base.OnTearDown(); + _session.Dispose(); + _session = null; + } + } +} diff --git a/src/NHibernate.Test/TypesTest/LocalDateTimeClass.hbm.xml b/src/NHibernate.Test/TypesTest/LocalDateTimeClass.hbm.xml new file mode 100644 index 00000000000..3c7030daf1b --- /dev/null +++ b/src/NHibernate.Test/TypesTest/LocalDateTimeClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/LocalDateTimeNoMsClass.hbm.xml b/src/NHibernate.Test/TypesTest/LocalDateTimeNoMsClass.hbm.xml new file mode 100644 index 00000000000..e6951be3c42 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/LocalDateTimeNoMsClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/LocalDateTimeTypeFixture.cs b/src/NHibernate.Test/TypesTest/LocalDateTimeTypeFixture.cs index 5e343f2b69e..395c1e1ec36 100644 --- a/src/NHibernate.Test/TypesTest/LocalDateTimeTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/LocalDateTimeTypeFixture.cs @@ -1,43 +1,29 @@ -using System; +using NHibernate.Type; using NUnit.Framework; namespace NHibernate.Test.TypesTest { /// - /// The Unit Tests for the UtcDateTimeType. + /// The Unit Tests for the LocalDateTimeType. /// [TestFixture] - public class LocalDateTimeTypeFixture : TypeFixtureBase + public class LocalDateTimeTypeFixture : AbstractDateTimeTypeFixture { - protected override string TypeName - { - get { return "DateTime"; } - } - - [Test] - public void ReadWrite() - { - DateTime val = DateTime.UtcNow; - DateTime expected = new DateTime(val.Year, val.Month, val.Day, val.Hour, val.Minute, val.Second, DateTimeKind.Local); - - DateTimeClass basic = new DateTimeClass(); - basic.Id = 1; - basic.LocalDateTimeValue = val; - - ISession s = OpenSession(); - s.Save(basic); - s.Flush(); - s.Close(); - - s = OpenSession(); - basic = (DateTimeClass) s.Load(typeof (DateTimeClass), 1); + protected override string TypeName => "LocalDateTime"; + protected override AbstractDateTimeType Type => NHibernateUtil.LocalDateTime; + } - Assert.AreEqual(DateTimeKind.Local, basic.LocalDateTimeValue.Value.Kind); - Assert.AreEqual(expected, basic.LocalDateTimeValue.Value); + [TestFixture] + public class LocalDateTimeTypeWithScaleFixture : DateTimeTypeWithScaleFixture + { + protected override string TypeName => "LocalDateTimeWithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetLocalDateTimeType(3); + } - s.Delete(basic); - s.Flush(); - s.Close(); - } + [TestFixture] + public class LocalDateTimeNoMsTypeFixture : DateTimeNoMsTypeFixture + { + protected override string TypeName => "LocalDateTimeNoMs"; + protected override AbstractDateTimeType Type => NHibernateUtil.LocalDateTimeNoMs; } } diff --git a/src/NHibernate.Test/TypesTest/LocalDateTimeWithScaleClass.hbm.xml b/src/NHibernate.Test/TypesTest/LocalDateTimeWithScaleClass.hbm.xml new file mode 100644 index 00000000000..994e7a6865a --- /dev/null +++ b/src/NHibernate.Test/TypesTest/LocalDateTimeWithScaleClass.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/MultiTypeEntity_Defined.hbm.xml b/src/NHibernate.Test/TypesTest/MultiTypeEntity_Defined.hbm.xml index 2741ad92efb..482a0fe53f6 100644 --- a/src/NHibernate.Test/TypesTest/MultiTypeEntity_Defined.hbm.xml +++ b/src/NHibernate.Test/TypesTest/MultiTypeEntity_Defined.hbm.xml @@ -1,20 +1,26 @@  + namespace="NHibernate.Test.TypesTest" + assembly="NHibernate.Test"> - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/MultiTypeEntity_Heuristic.hbm.xml b/src/NHibernate.Test/TypesTest/MultiTypeEntity_Heuristic.hbm.xml index ec2d3d59378..ff455625e25 100644 --- a/src/NHibernate.Test/TypesTest/MultiTypeEntity_Heuristic.hbm.xml +++ b/src/NHibernate.Test/TypesTest/MultiTypeEntity_Heuristic.hbm.xml @@ -1,20 +1,26 @@  + namespace="NHibernate.Test.TypesTest" + assembly="NHibernate.Test"> - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/MultiTypeEntity_InLine.hbm.xml b/src/NHibernate.Test/TypesTest/MultiTypeEntity_InLine.hbm.xml index 3548f3954fd..956a212a3fb 100644 --- a/src/NHibernate.Test/TypesTest/MultiTypeEntity_InLine.hbm.xml +++ b/src/NHibernate.Test/TypesTest/MultiTypeEntity_InLine.hbm.xml @@ -1,20 +1,26 @@  + namespace="NHibernate.Test.TypesTest" + assembly="NHibernate.Test"> - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithColumnNode.hbm.xml b/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithColumnNode.hbm.xml index 695bb179a1c..cced6babb3b 100644 --- a/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithColumnNode.hbm.xml +++ b/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithColumnNode.hbm.xml @@ -1,38 +1,56 @@  + namespace="NHibernate.Test.TypesTest" + assembly="NHibernate.Test"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithSqlType.hbm.xml b/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithSqlType.hbm.xml index 10c2718331d..2d05eb0aee6 100644 --- a/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithSqlType.hbm.xml +++ b/src/NHibernate.Test/TypesTest/MultiTypeEntity_WithSqlType.hbm.xml @@ -1,38 +1,56 @@  + namespace="NHibernate.Test.TypesTest" + assembly="NHibernate.Test"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/PersistentEnumTypeFixture.cs b/src/NHibernate.Test/TypesTest/PersistentEnumTypeFixture.cs index 50a1818d262..f950e3326cf 100644 --- a/src/NHibernate.Test/TypesTest/PersistentEnumTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/PersistentEnumTypeFixture.cs @@ -136,6 +136,7 @@ public void CanWriteAndReadUsingBothHeuristicAndExplicitGenericDeclaration() } } + [TestFixture] public class GenericEnumTypeTest { [Test] diff --git a/src/NHibernate.Test/TypesTest/StringTypeWithLengthFixture.cs b/src/NHibernate.Test/TypesTest/StringTypeWithLengthFixture.cs index 3717c0f746f..e3e2367f0ea 100644 --- a/src/NHibernate.Test/TypesTest/StringTypeWithLengthFixture.cs +++ b/src/NHibernate.Test/TypesTest/StringTypeWithLengthFixture.cs @@ -5,7 +5,6 @@ using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Exceptions; -using NHibernate.Linq; using NHibernate.Mapping.ByCode; using NUnit.Framework; @@ -50,6 +49,16 @@ protected override HbmMapping GetMappings() return mapper.CompileMappingForAllExplicitlyAddedEntities(); } + protected override void OnTearDown() + { + base.OnTearDown(); + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from System.Object").ExecuteUpdate(); + t.Commit(); + } + } [Test] [Description("Values longer than the maximum possible string length " + @@ -81,6 +90,57 @@ public void ShouldPreventInsertionOfVeryLongStringThatWouldBeTruncated() AssertFailedInsertExceptionDetailsAndEmptyTable(ex); } + // NH-4083 + [Test] + public void CanCompareShortValueWithLongString() + { + var maxStringLength = GetLongStringMappedLength(); + var longString = new string('x', maxStringLength); + using (var s = OpenSession()) + { + var b = new StringClass { LongStringValue = longString }; + s.Save(b); + s.Flush(); + } + + using (var s = OpenSession()) + { + var q = s.CreateQuery("from StringClass s where s.LongStringValue != :shortString") + // Do not replace with SetString, otherwise length will be unspecified. + .SetParameter("shortString", "aaa"); + var sc = q.UniqueResult(); + Assert.That(sc, Is.Not.Null); + Assert.That(sc.LongStringValue, Is.EqualTo(longString)); + } + } + + [Test] + public void CanCompareLongValueWithLongString() + { + var maxStringLength = GetLongStringMappedLength(); + + if (Sfi.ConnectionProvider.Driver is OdbcDriver && maxStringLength >= 2000) + Assert.Ignore("Odbc wrecks nvarchar parameter types when they are longer than 2000, it switch them to ntext"); + + var longString = new string('x', maxStringLength); + using (var s = OpenSession()) + { + var b = new StringClass { LongStringValue = longString }; + s.Save(b); + s.Flush(); + } + + using (var s = OpenSession()) + { + var q = s.CreateQuery("from StringClass s where s.LongStringValue = :longString") + // Do not replace with SetString, otherwise length will be unspecified. + .SetParameter("longString", longString); + var sc = q.UniqueResult(); + Assert.That(sc, Is.Not.Null); + Assert.That(sc.LongStringValue, Is.EqualTo(longString)); + } + } + [Test] [Description("Values longer than the mapped string length " + "should raise an exception if they would otherwise be truncated.")] @@ -107,7 +167,6 @@ public void ShouldPreventInsertionOfTooLongStringThatWouldBeTruncated() AssertFailedInsertExceptionDetailsAndEmptyTable(ex); } - private void AssertFailedInsertExceptionDetailsAndEmptyTable(Exception ex) { // We can get different sort of exceptions. @@ -145,164 +204,92 @@ private void AssertFailedInsertExceptionDetailsAndEmptyTable(Exception ex) } } - [Test] public void CriteriaLikeParameterCanExceedColumnSize() { - Exception exception = CatchException( - () => - { - using (ISession s = OpenSession()) - using (s.BeginTransaction()) - { - s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); - s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); - - var aaItems = - s.CreateCriteria() - .Add(Restrictions.Like("StringValue", "%AAAAAAAAA%")) - .List(); - - Assert.That(aaItems.Count, Is.EqualTo(2)); - } - }); + using (ISession s = OpenSession()) + using (s.BeginTransaction()) + { + s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); + s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); + var aaItems = + s.CreateCriteria() + .Add(Restrictions.Like("StringValue", "%AAAAAAAAA%")) + .List(); - // This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column. - AssertExpectedFailureOrNoException( - exception, - (Sfi.ConnectionProvider.Driver is OdbcDriver)); + Assert.That(aaItems.Count, Is.EqualTo(2)); + } } [Test] public void HqlLikeParameterCanExceedColumnSize() { - Exception exception = CatchException( - () => - { - using (ISession s = OpenSession()) - using (s.BeginTransaction()) - { - s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); - s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); - - var aaItems = - s.CreateQuery("from StringClass s where s.StringValue like :likeValue") - .SetParameter("likeValue", "%AAAAAAAAA%") - .List(); - - Assert.That(aaItems.Count, Is.EqualTo(2)); - } - }); + using (ISession s = OpenSession()) + using (s.BeginTransaction()) + { + s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); + s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); + var aaItems = + s.CreateQuery("from StringClass s where s.StringValue like :likeValue") + .SetParameter("likeValue", "%AAAAAAAAA%") + .List(); - // This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column. - AssertExpectedFailureOrNoException( - exception, - (Sfi.ConnectionProvider.Driver is OdbcDriver)); + Assert.That(aaItems.Count, Is.EqualTo(2)); + } } - [Test] public void CriteriaEqualityParameterCanExceedColumnSize() { + if (!TestDialect.SupportsNonDataBoundCondition) + { + // Doesn't work on Firebird due to Firebird not figuring out parameter types on its own. + Assert.Ignore("Dialect does not support this test"); + } + // We should be able to query a column with a value longer than // the specified column size, to avoid tedious exceptions. + using (ISession s = OpenSession()) + using (s.BeginTransaction()) + { + s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); + s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); - Exception exception = CatchException( - () => - { - using (ISession s = OpenSession()) - using (s.BeginTransaction()) - { - s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); - s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); - - var aaItems = - s.CreateCriteria() - .Add(Restrictions.Eq("StringValue", "AAAAAAAAABx")) - .List(); - - Assert.That(aaItems.Count, Is.EqualTo(0)); - } - }); + var aaItems = + s.CreateCriteria() + .Add(Restrictions.Eq("StringValue", "AAAAAAAAABx")) + .List(); - // Doesn't work on Firebird due to Firebird not figuring out parameter types on its own. - // This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column. - AssertExpectedFailureOrNoException( - exception, - (Dialect is FirebirdDialect) || (Sfi.ConnectionProvider.Driver is OdbcDriver)); + Assert.That(aaItems.Count, Is.EqualTo(0)); + } } - [Test] public void HqlEqualityParameterCanExceedColumnSize() { - // We should be able to query a column with a value longer than - // the specified column size, to avoid tedious exceptions. - - Exception exception = CatchException( - () => - { - using (ISession s = OpenSession()) - using (s.BeginTransaction()) - { - s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); - s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); - - var aaItems = - s.CreateQuery("from StringClass s where s.StringValue = :likeValue") - .SetParameter("likeValue", "AAAAAAAAABx") - .List(); - - Assert.That(aaItems.Count, Is.EqualTo(0)); - } - }); - - // Doesn't work on Firebird due to Firebird not figuring out parameter types on its own. - // This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column. - AssertExpectedFailureOrNoException( - exception, - (Dialect is FirebirdDialect) || (Sfi.ConnectionProvider.Driver is OdbcDriver)); - } - - - /// - /// Some test cases doesn't work during some scenarios for well-known reasons. If the test - /// fails under these circumstances, mark it as IGNORED. If it _stops_ failing, mark it - /// as a FAILURE so that it can be investigated. - /// - private void AssertExpectedFailureOrNoException(Exception exception, bool requireExceptionAndIgnoreTest) - { - if (requireExceptionAndIgnoreTest) + if (!TestDialect.SupportsNonDataBoundCondition) { - Assert.NotNull( - exception, - "Test was expected to have a well-known, but ignored, failure for the current configuration. If " + - "that expected failure no longer occurs, it may now be possible to remove this exception."); - - Assert.Ignore("This test is known to fail for the current configuration."); + // Doesn't work on Firebird due to Firebird not figuring out parameter types on its own. + Assert.Ignore("Dialect does not support this test"); } - // If the above didn't ignore the exception, it's for real - rethrow to trigger test failure. - if (exception != null) - throw new Exception("Wrapped exception.", exception); - } + // We should be able to query a column with a value longer than + // the specified column size, to avoid tedious exceptions. + using (ISession s = OpenSession()) + using (s.BeginTransaction()) + { + s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"}); + s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"}); + var aaItems = + s.CreateQuery("from StringClass s where s.StringValue = :likeValue") + .SetParameter("likeValue", "AAAAAAAAABx") + .List(); - private TException CatchException(System.Action action) - where TException : Exception - { - try - { - action(); + Assert.That(aaItems.Count, Is.EqualTo(0)); } - catch (TException exception) - { - return exception; - } - - return null; } } } diff --git a/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.cs b/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.cs index 98c545ebb52..5aea5c41102 100644 --- a/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.cs +++ b/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.cs @@ -6,5 +6,6 @@ public class TimeAsTimeSpanClass { public int Id { get; set; } public TimeSpan TimeSpanValue { get; set; } + public TimeSpan TimeSpanWithScale { get; set; } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.hbm.xml b/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.hbm.xml index f200f0c6aca..44a2d252dda 100644 --- a/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.hbm.xml +++ b/src/NHibernate.Test/TypesTest/TimeAsTimeSpanClass.hbm.xml @@ -1,11 +1,12 @@ - + - - - - - - - - + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/TimeAsTimeSpanTypeFixture.cs b/src/NHibernate.Test/TypesTest/TimeAsTimeSpanTypeFixture.cs index dfece19584a..5c0e2c364f0 100644 --- a/src/NHibernate.Test/TypesTest/TimeAsTimeSpanTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/TimeAsTimeSpanTypeFixture.cs @@ -38,6 +38,30 @@ protected override string TypeName get { return "TimeAsTimeSpan"; } } + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from TimeAsTimeSpanClass").ExecuteUpdate(); + tx.Commit(); + } + } + + [Test] + public void PropertiesHasExpectedType() + { + var classMetaData = Sfi.GetClassMetadata(typeof(TimeAsTimeSpanClass)); + Assert.That( + classMetaData.GetPropertyType(nameof(TimeAsTimeSpanClass.TimeSpanValue)), + Is.EqualTo(NHibernateUtil.TimeAsTimeSpan)); + Assert.That( + classMetaData.GetPropertyType(nameof(TimeAsTimeSpanClass.TimeSpanWithScale)), + Is.EqualTo(TypeFactory.GetTimeAsTimeSpanType(3))); + } + [Test] public void SavingAndRetrieving() { @@ -67,13 +91,37 @@ public void SavingAndRetrieving() Assert.AreEqual(entityReturned.TimeSpanValue.Minutes, ticks.Minutes); Assert.AreEqual(entityReturned.TimeSpanValue.Seconds, ticks.Seconds); } + } - using (ISession s = OpenSession()) - using (ITransaction tx = s.BeginTransaction()) + [Test] + public void LowerDigitsAreIgnored() + { + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseTime = new TimeSpan(0, 17, 55, 24, 548); + var entity = new TimeAsTimeSpanClass { - s.Delete(entityReturned); - tx.Commit(); + TimeSpanWithScale = baseTime.Add(TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond / 3)) + }; + Assert.That(entity.TimeSpanWithScale, Is.Not.EqualTo(baseTime)); + + int id; + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(entity); + id = entity.Id; + t.Commit(); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = s.Load(id); + Assert.That(retrieved.TimeSpanWithScale, Is.EqualTo(baseTime)); + t.Commit(); } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/TypesTest/TimeClass.cs b/src/NHibernate.Test/TypesTest/TimeClass.cs new file mode 100644 index 00000000000..8bb2c186146 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/TimeClass.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.TypesTest +{ + public class TimeClass + { + public int Id { get; set; } + public DateTime TimeValue { get; set; } + public DateTime TimeWithScale { get; set; } + } +} diff --git a/src/NHibernate.Test/TypesTest/TimeClass.hbm.xml b/src/NHibernate.Test/TypesTest/TimeClass.hbm.xml new file mode 100644 index 00000000000..75751cb768b --- /dev/null +++ b/src/NHibernate.Test/TypesTest/TimeClass.hbm.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/TimeFixture.cs b/src/NHibernate.Test/TypesTest/TimeFixture.cs new file mode 100644 index 00000000000..1fd8e36cba2 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/TimeFixture.cs @@ -0,0 +1,98 @@ +using System; +using NHibernate.Type; +using NUnit.Framework; + +namespace NHibernate.Test.TypesTest +{ + [TestFixture] + public class TimeFixture : TypeFixtureBase + { + protected override string TypeName => "Time"; + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from TimeClass").ExecuteUpdate(); + tx.Commit(); + } + } + + [Test] + public void PropertiesHasExpectedType() + { + var classMetaData = Sfi.GetClassMetadata(typeof(TimeClass)); + Assert.That( + classMetaData.GetPropertyType(nameof(TimeClass.TimeValue)), + Is.EqualTo(NHibernateUtil.Time)); + Assert.That( + classMetaData.GetPropertyType(nameof(TimeClass.TimeWithScale)), + Is.EqualTo(TypeFactory.GetTimeType(3))); + } + + [Test] + public void SavingAndRetrieving() + { + var ticks = DateTime.Parse("23:59:59"); + + var entity = + new TimeClass + { + TimeValue = ticks + }; + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.Save(entity); + tx.Commit(); + } + + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + var entityReturned = s.CreateQuery("from TimeClass").UniqueResult(); + + Assert.That(entityReturned.TimeValue.TimeOfDay, Is.EqualTo(ticks.TimeOfDay)); + Assert.That(ticks.Hour, Is.EqualTo(entityReturned.TimeValue.Hour)); + Assert.That(ticks.Minute, Is.EqualTo(entityReturned.TimeValue.Minute)); + Assert.That(ticks.Second, Is.EqualTo(entityReturned.TimeValue.Second)); + tx.Commit(); + } + } + + [Test] + public void LowerDigitsAreIgnored() + { + if (!Dialect.SupportsDateTimeScale) + Assert.Ignore("Lower digits cannot be ignored when dialect does not support scale"); + + var baseTime = new DateTime(1990, 01, 01, 17, 55, 24, 548); + var entity = new TimeClass + { + TimeWithScale = baseTime.Add(TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond / 3)) + }; + Assert.That(entity.TimeWithScale, Is.Not.EqualTo(baseTime)); + + int id; + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(entity); + id = entity.Id; + t.Commit(); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var retrieved = s.Load(id); + Assert.That(retrieved.TimeWithScale.TimeOfDay, Is.EqualTo(baseTime.TimeOfDay)); + t.Commit(); + } + } + } +} diff --git a/src/NHibernate.Test/TypesTest/TimestampClass.hbm.xml b/src/NHibernate.Test/TypesTest/TimestampClass.hbm.xml new file mode 100644 index 00000000000..0df150ff5f8 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/TimestampClass.hbm.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/TimestampTypeFixture.cs b/src/NHibernate.Test/TypesTest/TimestampTypeFixture.cs index 446640d0bd8..5cd6b53d3fc 100644 --- a/src/NHibernate.Test/TypesTest/TimestampTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/TimestampTypeFixture.cs @@ -4,29 +4,32 @@ namespace NHibernate.Test.TypesTest { - /// - /// Summary description for TimestampTypeFixture. - /// [TestFixture] - public class TimestampTypeFixture + [Obsolete] + public class TimestampTypeFixture : AbstractDateTimeTypeFixture { - [Test] - public void Next() - { - TimestampType type = (TimestampType) NHibernateUtil.Timestamp; - object current = DateTime.Parse("2004-01-01"); - object next = type.Next(current, null); - - Assert.IsTrue(next is DateTime, "Next should be DateTime"); - Assert.IsTrue((DateTime) next > (DateTime) current, - "next should be greater than current (could be equal depending on how quickly this occurs)"); - } + protected override string TypeName => "Timestamp"; + protected override AbstractDateTimeType Type => NHibernateUtil.Timestamp; [Test] - public void Seed() + public void ObsoleteMessage() { - TimestampType type = (TimestampType) NHibernateUtil.Timestamp; - Assert.IsTrue(type.Seed(null) is DateTime, "seed should be DateTime"); + using (var spy = new LogSpy(typeof(TypeFactory))) + { + var config = TestConfigurationHelper.GetDefaultConfiguration(); + AddMappings(config); + Configure(config); + using (config.BuildSessionFactory()) + { + var log = spy.GetWholeLog(); + Assert.That( + log, + Does.Contain("NHibernate.Type.TimestampType is obsolete. Please use DateTimeType instead.").IgnoreCase); + Assert.That( + log, + Does.Not.Contain($"{NHibernateUtil.Timestamp.Name} is obsolete. Please use DateTimeType instead.").IgnoreCase); + } + } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/TypesTest/TimestampUtcClass.cs b/src/NHibernate.Test/TypesTest/TimestampUtcClass.cs deleted file mode 100644 index 1adc6022462..00000000000 --- a/src/NHibernate.Test/TypesTest/TimestampUtcClass.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace NHibernate.Test.TypesTest -{ - public class TimestampUtcClass - { - public int Id { get; set; } - public DateTime Value { get; set; } - public DateTime Revision { get; protected set; } - } -} diff --git a/src/NHibernate.Test/TypesTest/TimestampUtcClass.hbm.xml b/src/NHibernate.Test/TypesTest/TimestampUtcClass.hbm.xml deleted file mode 100644 index 9257e6313fe..00000000000 --- a/src/NHibernate.Test/TypesTest/TimestampUtcClass.hbm.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/src/NHibernate.Test/TypesTest/TimestampUtcTypeFixture.cs b/src/NHibernate.Test/TypesTest/TimestampUtcTypeFixture.cs deleted file mode 100644 index 9b37ea154fe..00000000000 --- a/src/NHibernate.Test/TypesTest/TimestampUtcTypeFixture.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using NHibernate.Type; -using NUnit.Framework; - -namespace NHibernate.Test.TypesTest -{ - /// - /// Test fixture for type . - /// - [TestFixture] - public class TimestampUtcTypeFixture : TypeFixtureBase - { - readonly TimestampUtcType _type = NHibernateUtil.TimestampUtc; - readonly DateTime _utc = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Utc); - readonly DateTime _local = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Local); - readonly DateTime _unspecified = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Unspecified); - - /// - /// 1976-11-30T10:00:00.3000000 - /// - const long DateInTicks = 623537928003000000; - - protected override string TypeName => "TimestampUtc"; - - [Test] - public void Next() - { - var current = DateTime.Parse("2004-01-01"); - var next = (DateTime)_type.Next(current, null); - - Assert.AreEqual(DateTimeKind.Utc, next.Kind, "Kind is not Utc"); - Assert.IsTrue(next > current, "next should be greater than current (could be equal depending on how quickly this occurs)"); - } - - /// - /// Perform a 'seed' and check if the result is a datetime with kind set to Utc. - /// - [Test] - public void Seed() - { - var type = NHibernateUtil.TimestampUtc; - Assert.IsTrue(type.Seed(null) is DateTime, "Seed should be DateTime"); - - var value = (DateTime)type.Seed(null); - Assert.AreEqual(DateTimeKind.Utc, value.Kind, "Kind should be Utc"); - } - - /// - /// Perform a basis write with a DateTime value where Kind is Local which should fail. - /// - [Test] - [TestCase(DateTimeKind.Unspecified)] - [TestCase(DateTimeKind.Local)] - public void LocalReadWrite_Fail(DateTimeKind kind) - { - var entity = new TimestampUtcClass - { - Id = 1, - Value = DateTime.SpecifyKind(DateTime.Now, kind) - }; - - using(var session = OpenSession()) - using(var tx = session.BeginTransaction()) - { - session.Save(entity); - Assert.That(() => session.Flush(), Throws.TypeOf()); - tx.Rollback(); - } - } - - /// - /// Create two session. Write entity in the first and read it in the second and compare if - /// the retrieved timestamp value still equals the original value. - /// - /// This test takes the database precision into consideration. - [Test] - public void UtcReadWrite_Success() - { - TimestampUtcClass entity; - - // Save - using(var session = OpenSession()) - using(var tx = session.BeginTransaction()) - { - // Create a new datetime value and round it to the precision that the database supports. This - // code basically the same as in the implementation but here to guard posible changes. - var resolution = session.GetSessionImplementation().Factory.Dialect.TimestampResolutionInTicks; - var next = DateTime.UtcNow; - next = next.AddTicks(-(next.Ticks % resolution)); - - entity = new TimestampUtcClass - { - Id = 1, - Value = next - }; - - session.Save(entity); - tx.Commit(); - session.Close(); - } - - // Retrieve and compare - using (var session = OpenSession()) - using (var tx = session.BeginTransaction()) - { - var result = session.Get(entity.Id); - Assert.IsNotNull(result, "Entity not saved or cannot be retrieved by its key."); - - // Property: Value - Assert.AreEqual(DateTimeKind.Utc, result.Value.Kind, "Kind is NOT Utc"); - Assert.AreEqual(entity.Value.Ticks, result.Value.Ticks, "Value should be the same."); - - // Property: Revision - var revision = result.Revision; - Assert.AreEqual(DateTimeKind.Utc, revision.Kind, "Kind is NOT Utc"); - - var differenceInMinutes = Math.Abs((revision - DateTime.UtcNow).TotalMinutes); - // Take a wide margin for accounting for sometimes bad build servers performances. - Assert.Less(differenceInMinutes, 2, "Difference should be less than 2 minutes."); - - tx.Commit(); - session.Close(); - } - - // Delete - using (var session = OpenSession()) - using (var tx = session.BeginTransaction()) - { - var result = session.Get(entity.Id); - session.Delete(result); - tx.Commit(); - session.Close(); - } - } - - /// - /// Tests if the type FromStringValue implementation behaves as expected. - /// - /// - [Test] - [TestCase("2011-01-27T15:50:59.6220000+02:00")] - [TestCase("2011-01-27T14:50:59.6220000+01:00")] - [TestCase("2011-01-27T13:50:59.6220000Z")] - public void FromStringValue_ParseValidValues(string timestampValue) - { - var timestamp = DateTime.Parse(timestampValue); - - Assert.AreEqual(DateTimeKind.Local, timestamp.Kind, "Kind is NOT Local. dotnet framework parses datetime values with kind set to Local and time correct to local timezone."); - - timestamp = timestamp.ToUniversalTime(); - - var value = (DateTime)_type.FromStringValue(timestampValue); - - Assert.AreEqual(timestamp, value, timestampValue); - Assert.AreEqual(DateTimeKind.Utc, value.Kind, "Kind is NOT Utc"); - } - - /// - /// Test the framework tostring behavior. If the test fails then the and implemention could not work propertly at run-time. - /// - [Test, Category("Expected framework behavior")] - [TestCase(623537928003000000, DateTimeKind.Utc, ExpectedResult = "1976-11-30T10:00:00.3000000Z")] - [TestCase(623537928003000000, DateTimeKind.Unspecified, ExpectedResult = "1976-11-30T10:00:00.3000000")] - [TestCase(623537928003000000, DateTimeKind.Local, ExpectedResult = "1976-11-30T10:00:00.3000000+01:00", - Ignore = "Offset depends on which system this test is run and can currently now be influenced via the .net framework", - Description ="This test will ONLY succeed when the test is run on a system which if currently in a timezone with offset +01:00")] - public string ExpectedToStringDotnetFrameworkBehavior(long ticks, DateTimeKind kind) - { - return new DateTime(ticks, kind).ToString("o"); - } - - /// - /// Test the framework tostring behavior. If the test fails then the and implemention could not work propertly at run-time. - /// - [Test, Category("Expected framework behavior")] - public void ExpectedIsEqualDotnetFrameworkBehavior() - { - const string assertMessage = "Values should be equal dotnet framework ignores Kind value."; - Assert.AreEqual(_utc, _local, assertMessage); - Assert.AreEqual(_utc, _unspecified, assertMessage); - Assert.AreEqual(_unspecified, _local, assertMessage); - } - } -} diff --git a/src/NHibernate.Test/TypesTest/TypeSqlTypeFixture.cs b/src/NHibernate.Test/TypesTest/TypeSqlTypeFixture.cs index caa01e36486..88b4104dd2a 100644 --- a/src/NHibernate.Test/TypesTest/TypeSqlTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/TypeSqlTypeFixture.cs @@ -1,3 +1,4 @@ +using System; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Engine; @@ -17,8 +18,15 @@ public class MultiTypeEntity public virtual byte[] BinaryBlob { get; set; } public virtual byte[] Binary { get; set; } public virtual string StringClob { get; set; } + public virtual DateTime DateTimeProp { get; set; } + public virtual DateTime LocalDateTime { get; set; } + public virtual DateTime UtcDateTime { get; set; } + public virtual DateTime TimeProp { get; set; } + public virtual TimeSpan TimeAsTimeSpan { get; set; } + public virtual DateTimeOffset DateTimeOffsetProp { get; set; } } + [TestFixture] public abstract class TypeSqlTypeFixture { protected const string TestNameSpace = "NHibernate.Test.TypesTest."; @@ -82,6 +90,25 @@ public void NotIgnoreSqlTypeDef() type = pc.GetPropertyType("StringClob"); Assert.That(type.SqlTypes(factory)[0].Length, Is.EqualTo(1002)); + + type = pc.GetPropertyType(nameof(MultiTypeEntity.DateTimeProp)); + Assert.That(type.SqlTypes(factory)[0].Scale, Is.EqualTo(0), "Unexpected scale for DateTimeProp"); + Assert.That(type.SqlTypes(factory)[0].ScaleDefined, Is.True); + + type = pc.GetPropertyType(nameof(MultiTypeEntity.LocalDateTime)); + Assert.That(type.SqlTypes(factory)[0].Scale, Is.EqualTo(1), "Unexpected scale for LocalDateTime"); + + type = pc.GetPropertyType(nameof(MultiTypeEntity.UtcDateTime)); + Assert.That(type.SqlTypes(factory)[0].Scale, Is.EqualTo(2), "Unexpected scale for UtcDateTime"); + + type = pc.GetPropertyType(nameof(MultiTypeEntity.TimeProp)); + Assert.That(type.SqlTypes(factory)[0].Scale, Is.EqualTo(3), "Unexpected scale for TimeProp"); + + type = pc.GetPropertyType(nameof(MultiTypeEntity.TimeAsTimeSpan)); + Assert.That(type.SqlTypes(factory)[0].Scale, Is.EqualTo(4), "Unexpected scale for TimeAsTimeSpan"); + + type = pc.GetPropertyType(nameof(MultiTypeEntity.DateTimeOffsetProp)); + Assert.That(type.SqlTypes(factory)[0].Scale, Is.EqualTo(5), "Unexpected scale for DateTimeOffsetProp"); } protected abstract string GetResourceName(); @@ -128,13 +155,12 @@ protected override string GetResourceName() } } - [TestFixture, Ignore("Not fixed yet.")] public class FixtureWithSqlType : TypeSqlTypeFixture { protected override bool AppliesTo(Dialect.Dialect dialect) { - return dialect is MsSql2000Dialect; + return dialect is MsSql2008Dialect; } protected override string GetResourceName() { diff --git a/src/NHibernate.Test/TypesTest/UtcDateTimeClass.hbm.xml b/src/NHibernate.Test/TypesTest/UtcDateTimeClass.hbm.xml new file mode 100644 index 00000000000..dbfec02cc91 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/UtcDateTimeClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/UtcDateTimeNoMsClass.hbm.xml b/src/NHibernate.Test/TypesTest/UtcDateTimeNoMsClass.hbm.xml new file mode 100644 index 00000000000..da4c32f1d57 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/UtcDateTimeNoMsClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/NHibernate.Test/TypesTest/UtcDateTimeTypeFixture.cs b/src/NHibernate.Test/TypesTest/UtcDateTimeTypeFixture.cs index e59e624411a..42c69b8767f 100644 --- a/src/NHibernate.Test/TypesTest/UtcDateTimeTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/UtcDateTimeTypeFixture.cs @@ -1,4 +1,4 @@ -using System; +using NHibernate.Type; using NUnit.Framework; namespace NHibernate.Test.TypesTest @@ -7,37 +7,23 @@ namespace NHibernate.Test.TypesTest /// The Unit Tests for the UtcDateTimeType. /// [TestFixture] - public class UtcDateTimeTypeFixture : TypeFixtureBase + public class UtcDateTimeTypeFixture : AbstractDateTimeTypeFixture { - protected override string TypeName - { - get { return "DateTime"; } - } - - [Test] - public void ReadWrite() - { - DateTime val = DateTime.UtcNow; - DateTime expected = new DateTime(val.Year, val.Month, val.Day, val.Hour, val.Minute, val.Second, DateTimeKind.Utc); - - DateTimeClass basic = new DateTimeClass(); - basic.Id = 1; - basic.UtcDateTimeValue = val; - - ISession s = OpenSession(); - s.Save(basic); - s.Flush(); - s.Close(); - - s = OpenSession(); - basic = (DateTimeClass) s.Load(typeof (DateTimeClass), 1); + protected override string TypeName => "UtcDateTime"; + protected override AbstractDateTimeType Type => NHibernateUtil.UtcDateTime; + } - Assert.AreEqual(DateTimeKind.Utc, basic.UtcDateTimeValue.Value.Kind); - Assert.AreEqual(expected, basic.UtcDateTimeValue.Value); + [TestFixture] + public class UtcDateTimeTypeWithScaleFixture : DateTimeTypeWithScaleFixture + { + protected override string TypeName => "UtcDateTimeWithScale"; + protected override AbstractDateTimeType Type => (AbstractDateTimeType)TypeFactory.GetUtcDateTimeType(3); + } - s.Delete(basic); - s.Flush(); - s.Close(); - } + [TestFixture] + public class UtcDateTimeNoMsTypeFixture : DateTimeNoMsTypeFixture + { + protected override string TypeName => "UtcDateTimeNoMs"; + protected override AbstractDateTimeType Type => NHibernateUtil.UtcDateTimeNoMs; } } diff --git a/src/NHibernate.Test/TypesTest/UtcDateTimeWithScaleClass.hbm.xml b/src/NHibernate.Test/TypesTest/UtcDateTimeWithScaleClass.hbm.xml new file mode 100644 index 00000000000..005eec87910 --- /dev/null +++ b/src/NHibernate.Test/TypesTest/UtcDateTimeWithScaleClass.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs b/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs index fdd55157703..6b4cbf2d586 100644 --- a/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs +++ b/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs @@ -1,8 +1,6 @@ using System; using System.Collections; using System.Threading; -using NHibernate.Driver; -using NHibernate.Engine; using NUnit.Framework; namespace NHibernate.Test.VersionTest.Db @@ -20,25 +18,6 @@ protected override string MappingsAssembly get { return "NHibernate.Test"; } } - protected override bool AppliesTo(ISessionFactoryImplementor factory) - { - // ODBC driver DateTime handling with SQL Server 2008+ Client is broken and forbids using it as a time stamp - // generated on db-side. - // Due to NH-3895, we have to force the scale on date-time parameters to 3 (3 significant fractional seconds digits) - // when using ODBC + SQL Server 2008+, otherwise DateTime values having milliseconds will be rejected. But the SQL - // Server DateTime does not have actually a one millisecond resolution (it has 3.333), causing ODBC to convert the - // parameter to DateTime2. A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is - // to be transmitted as having 3ms or 7ms and will match if transmitted as a DateTime. But when transmitted as - // DateTime2, it will no more be considered equal, causing the test to be flaky and failing two thirds of tries. - // Example failing update captured with profiler: - // exec sp_executesql N'UPDATE book SET name_column = @P1 WHERE id = @P2 AND version_column = @P3', - // N'@P1 nvarchar(18),@P2 int,@P3 datetime2',N'modified test book',1,'2017-08-02 16:37:16.0630000' - // Setting the scale to 2 still causes failure for two thirds of tries, due to 3ms/7ms being truncated in such case - // with ODBC and SQL Server 2008+ Client, which is rejected bu ODBC. - // (Affects NH-1756 too.) - return !(factory.ConnectionProvider.Driver is OdbcDriver); - } - [Test] public void CollectionVersion() { @@ -66,7 +45,7 @@ public void CollectionVersion() t.Commit(); s.Close(); - Assert.That(!NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); + Assert.That(!NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); guyTimestamp = guy.Timestamp; Thread.Sleep(1500); @@ -78,7 +57,7 @@ public void CollectionVersion() t.Commit(); s.Close(); - Assert.That(!NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); + Assert.That(!NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); s = OpenSession(); t = s.BeginTransaction(); @@ -111,7 +90,7 @@ public void CollectionNoVersion() s.Close(); const string ownerVersionWasIncremented = "owner version was incremented ({0:o} => {1:o})"; - Assert.That(NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), + Assert.That(NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), string.Format(ownerVersionWasIncremented, guyTimestamp, guy.Timestamp)); Console.WriteLine(string.Format(ownerVersionWasIncremented, guyTimestamp, guy.Timestamp)); @@ -122,7 +101,7 @@ public void CollectionNoVersion() t.Commit(); s.Close(); - Assert.That(NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), + Assert.That(NHibernateUtil.DbTimestamp.IsEqual(guyTimestamp, guy.Timestamp), string.Format(ownerVersionWasIncremented, guyTimestamp, guy.Timestamp)); s = OpenSession(); @@ -133,4 +112,4 @@ public void CollectionNoVersion() s.Close(); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/AdoNet/Util/SqlStatementLogger.cs b/src/NHibernate/AdoNet/Util/SqlStatementLogger.cs index 0d6f1a358c2..cc0dd277e79 100644 --- a/src/NHibernate/AdoNet/Util/SqlStatementLogger.cs +++ b/src/NHibernate/AdoNet/Util/SqlStatementLogger.cs @@ -104,11 +104,7 @@ public string GetCommandLineWithParameters(DbCommand command) private static string GetParameterLoggableType(DbParameter dataParameter) { - //TODO: Fix me after 4.6.2 update. Size and Precision has been added to DbParameter - var p = dataParameter as IDbDataParameter; - if (p != null) - return p.DbType + " (" + p.Size + ":" + p.Scale + ":" + p.Precision + ")"; - return dataParameter.DbType.ToString(); + return dataParameter.DbType + " (" + dataParameter.Size + ":" + dataParameter.Scale + ":" + dataParameter.Precision + ")"; } public string GetParameterLoggableValue(DbParameter parameter) diff --git a/src/NHibernate/Async/Dialect/InformixDialect.cs b/src/NHibernate/Async/Dialect/InformixDialect.cs index 2f1c52e0cb7..88d46a2335a 100644 --- a/src/NHibernate/Async/Dialect/InformixDialect.cs +++ b/src/NHibernate/Async/Dialect/InformixDialect.cs @@ -35,4 +35,4 @@ public override Task GetResultSetAsync(DbCommand statement, Cancel return statement.ExecuteReaderAsync(CommandBehavior.SingleResult, cancellationToken); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Dialect/SybaseASE15Dialect.cs b/src/NHibernate/Async/Dialect/SybaseASE15Dialect.cs index 25a3b7d111a..1b5cf7c4734 100644 --- a/src/NHibernate/Async/Dialect/SybaseASE15Dialect.cs +++ b/src/NHibernate/Async/Dialect/SybaseASE15Dialect.cs @@ -33,4 +33,4 @@ public override Task GetResultSetAsync(DbCommand statement, Cancel return statement.ExecuteReaderAsync(cancellationToken); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Dialect/SybaseSQLAnywhere10Dialect.cs b/src/NHibernate/Async/Dialect/SybaseSQLAnywhere10Dialect.cs index b26065ae75a..dd1dbb231d2 100644 --- a/src/NHibernate/Async/Dialect/SybaseSQLAnywhere10Dialect.cs +++ b/src/NHibernate/Async/Dialect/SybaseSQLAnywhere10Dialect.cs @@ -33,4 +33,4 @@ public override async Task GetResultSetAsync(DbCommand statement, #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/IMultiQuery.cs b/src/NHibernate/Async/IMultiQuery.cs index 69c98364adf..9d23828b73c 100644 --- a/src/NHibernate/Async/IMultiQuery.cs +++ b/src/NHibernate/Async/IMultiQuery.cs @@ -36,4 +36,4 @@ public partial interface IMultiQuery /// The instance for method chain. Task GetResultAsync(string key, CancellationToken cancellationToken = default(CancellationToken)); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/IQuery.cs b/src/NHibernate/Async/IQuery.cs index 49f94282e0b..31912e9ae7d 100644 --- a/src/NHibernate/Async/IQuery.cs +++ b/src/NHibernate/Async/IQuery.cs @@ -13,11 +13,11 @@ using NHibernate.Transform; using NHibernate.Type; using System.Collections.Generic; -using System.Threading; namespace NHibernate { using System.Threading.Tasks; + using System.Threading; public partial interface IQuery { diff --git a/src/NHibernate/Async/Impl/AbstractQueryImpl.cs b/src/NHibernate/Async/Impl/AbstractQueryImpl.cs index 26a452661cf..5740db48210 100644 --- a/src/NHibernate/Async/Impl/AbstractQueryImpl.cs +++ b/src/NHibernate/Async/Impl/AbstractQueryImpl.cs @@ -14,17 +14,16 @@ using NHibernate.Engine; using NHibernate.Engine.Query; using NHibernate.Hql; -using NHibernate.Properties; using NHibernate.Proxy; using NHibernate.Transform; using NHibernate.Type; using NHibernate.Util; using System.Linq; -using System.Threading; namespace NHibernate.Impl { using System.Threading.Tasks; + using System.Threading; public abstract partial class AbstractQueryImpl : IQuery { diff --git a/src/NHibernate/Async/Impl/MultiQueryImpl.cs b/src/NHibernate/Async/Impl/MultiQueryImpl.cs index b05bd3696a1..d6d69984a5b 100644 --- a/src/NHibernate/Async/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Async/Impl/MultiQueryImpl.cs @@ -277,4 +277,4 @@ private async Task ListUsingQueryCacheAsync(CancellationToken cancellatio #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 4e24fc47ca5..c3e9db3e1b6 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -825,7 +825,7 @@ protected internal virtual async Task PrepareQueryCommandAsync(QueryP IDriver driver = _factory.ConnectionProvider.Driver; driver.RemoveUnusedCommandParameters(command, sqlString); - driver.ExpandQueryParameters(command, sqlString); + driver.ExpandQueryParameters(command, sqlString, sqlCommand.ParameterTypes); } catch (HibernateException) { diff --git a/src/NHibernate/Async/Type/TimestampType.cs b/src/NHibernate/Async/Type/AbstractDateTimeType.cs similarity index 77% rename from src/NHibernate/Async/Type/TimestampType.cs rename to src/NHibernate/Async/Type/AbstractDateTimeType.cs index 25ad7b969d3..827ac034995 100644 --- a/src/NHibernate/Async/Type/TimestampType.cs +++ b/src/NHibernate/Async/Type/AbstractDateTimeType.cs @@ -10,7 +10,9 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Data.Common; +using System.Globalization; using NHibernate.Engine; using NHibernate.SqlTypes; @@ -18,20 +20,16 @@ namespace NHibernate.Type { using System.Threading.Tasks; using System.Threading; - public partial class TimestampType : PrimitiveType, IVersionType, ILiteralType + public abstract partial class AbstractDateTimeType : PrimitiveType, IIdentifierType, ILiteralType, IVersionType { #region IVersionType Members - public Task NextAsync(object current, ISessionImplementor session, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - return SeedAsync(session, cancellationToken); - } + /// + public Task NextAsync(object current, ISessionImplementor session, CancellationToken cancellationToken) => + SeedAsync(session, cancellationToken); + /// public virtual Task SeedAsync(ISessionImplementor session, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) diff --git a/src/NHibernate/Async/Type/AbstractType.cs b/src/NHibernate/Async/Type/AbstractType.cs index ae2ae556f68..25c02b7c6c2 100644 --- a/src/NHibernate/Async/Type/AbstractType.cs +++ b/src/NHibernate/Async/Type/AbstractType.cs @@ -248,4 +248,4 @@ public abstract Task ReplaceAsync(object original, object current, ISess public abstract Task IsDirtyAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Type/ComponentType.cs b/src/NHibernate/Async/Type/ComponentType.cs index b931d5d3c7e..7df3df3b71b 100644 --- a/src/NHibernate/Async/Type/ComponentType.cs +++ b/src/NHibernate/Async/Type/ComponentType.cs @@ -357,4 +357,4 @@ public override async Task IsModifiedAsync(object old, object current, boo return false; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Type/CompositeCustomType.cs b/src/NHibernate/Async/Type/CompositeCustomType.cs index fc00c45e14b..10e6590f7a6 100644 --- a/src/NHibernate/Async/Type/CompositeCustomType.cs +++ b/src/NHibernate/Async/Type/CompositeCustomType.cs @@ -181,4 +181,4 @@ public override Task ReplaceAsync(object original, object current, ISess } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Type/DateTime2Type.cs b/src/NHibernate/Async/Type/DateTimeNoMsType.cs similarity index 69% rename from src/NHibernate/Async/Type/DateTime2Type.cs rename to src/NHibernate/Async/Type/DateTimeNoMsType.cs index 32af8b4e89a..6a8276e1b39 100644 --- a/src/NHibernate/Async/Type/DateTime2Type.cs +++ b/src/NHibernate/Async/Type/DateTimeNoMsType.cs @@ -9,26 +9,17 @@ using System; -using System.Data; -using System.Data.Common; using NHibernate.Engine; -using NHibernate.SqlTypes; +using System.Data; namespace NHibernate.Type { using System.Threading.Tasks; using System.Threading; - public partial class DateTime2Type : DateTimeType + public partial class DateTimeNoMsType : AbstractDateTimeType { - public override Task NextAsync(object current, ISessionImplementor session, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - return SeedAsync(session, cancellationToken); - } + #region IVersionType Members public override Task SeedAsync(ISessionImplementor session, CancellationToken cancellationToken) { @@ -45,5 +36,7 @@ public override Task SeedAsync(ISessionImplementor session, Cancellation return Task.FromException(ex); } } + + #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Type/DateTimeOffSetType.cs b/src/NHibernate/Async/Type/DateTimeOffSetType.cs index 0b9940540e8..2eedce9041f 100644 --- a/src/NHibernate/Async/Type/DateTimeOffSetType.cs +++ b/src/NHibernate/Async/Type/DateTimeOffSetType.cs @@ -32,7 +32,8 @@ public Task NextAsync(object current, ISessionImplementor session, Cance return SeedAsync(session, cancellationToken); } - public Task SeedAsync(ISessionImplementor session, CancellationToken cancellationToken) + /// + public virtual Task SeedAsync(ISessionImplementor session, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { diff --git a/src/NHibernate/Async/Type/DateTimeType.cs b/src/NHibernate/Async/Type/DateTimeType.cs deleted file mode 100644 index b818cafe554..00000000000 --- a/src/NHibernate/Async/Type/DateTimeType.cs +++ /dev/null @@ -1,55 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by AsyncGenerator. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - - -using System; -using System.Collections; -using System.Data.Common; -using NHibernate.Engine; -using NHibernate.SqlTypes; -using System.Collections.Generic; -using System.Data; - -namespace NHibernate.Type -{ - using System.Threading.Tasks; - using System.Threading; - public partial class DateTimeType : PrimitiveType, IIdentifierType, ILiteralType, IVersionType - { - - #region IVersionType Members - - public virtual Task NextAsync(object current, ISessionImplementor session, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - return SeedAsync(session, cancellationToken); - } - - public virtual Task SeedAsync(ISessionImplementor session, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - try - { - return Task.FromResult(Seed(session)); - } - catch (Exception ex) - { - return Task.FromException(ex); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/src/NHibernate/Async/Type/DbTimestampType.cs b/src/NHibernate/Async/Type/DbTimestampType.cs index 11d288181da..e5d4cef61bb 100644 --- a/src/NHibernate/Async/Type/DbTimestampType.cs +++ b/src/NHibernate/Async/Type/DbTimestampType.cs @@ -11,7 +11,6 @@ using System; using System.Data; using System.Data.Common; - using NHibernate.Engine; using NHibernate.Exceptions; using NHibernate.Impl; @@ -22,94 +21,77 @@ namespace NHibernate.Type { using System.Threading.Tasks; using System.Threading; - public partial class DbTimestampType : TimestampType, IVersionType + public partial class DbTimestampType : AbstractDateTimeType { - public override Task SeedAsync(ISessionImplementor session, CancellationToken cancellationToken) + /// + public override async Task SeedAsync(ISessionImplementor session, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) + cancellationToken.ThrowIfCancellationRequested(); + if (session == null) { - return Task.FromCanceled(cancellationToken); + log.Debug("incoming session was null; using current application host time"); + return await (base.SeedAsync(null, cancellationToken)).ConfigureAwait(false); } - try + if (!session.Factory.Dialect.SupportsCurrentTimestampSelection) { - if (session == null) - { - log.Debug("incoming session was null; using current vm time"); - return base.SeedAsync(session, cancellationToken); - } - else if (!session.Factory.Dialect.SupportsCurrentTimestampSelection) - { - log.Debug("falling back to vm-based timestamp, as dialect does not support current timestamp selection"); - return base.SeedAsync(session, cancellationToken); - } - else - { - return GetCurrentTimestampAsync(session, cancellationToken); - } - } - catch (Exception ex) - { - return Task.FromException(ex); + log.Info("falling back to application host based timestamp, as dialect does not support current timestamp selection"); + return await (base.SeedAsync(session, cancellationToken)).ConfigureAwait(false); } + return await (GetCurrentTimestampAsync(session, cancellationToken)).ConfigureAwait(false); } - private Task GetCurrentTimestampAsync(ISessionImplementor session, CancellationToken cancellationToken) + protected virtual async Task GetCurrentTimestampAsync(ISessionImplementor session, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - try - { - Dialect.Dialect dialect = session.Factory.Dialect; - string timestampSelectString = dialect.CurrentTimestampSelectString; - return UsePreparedStatementAsync(timestampSelectString, session, cancellationToken); - } - catch (Exception ex) - { - return Task.FromException(ex); - } + cancellationToken.ThrowIfCancellationRequested(); + var dialect = session.Factory.Dialect; + // Need to round notably for Sql Server DateTime with Odbc, which has a 3.33ms resolution, + // causing stale data update failure 2/3 of times if not rounded to 10ms. + return Round( + await (UsePreparedStatementAsync(dialect.CurrentTimestampSelectString, session, cancellationToken)).ConfigureAwait(false), + dialect.TimestampResolutionInTicks); } - protected virtual async Task UsePreparedStatementAsync(string timestampSelectString, ISessionImplementor session, CancellationToken cancellationToken) + protected virtual async Task UsePreparedStatementAsync(string timestampSelectString, ISessionImplementor session, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var tsSelect = new SqlString(timestampSelectString); DbCommand ps = null; DbDataReader rs = null; - using (new SessionIdLoggingContext(session.SessionId)) - try + using (new SessionIdLoggingContext(session.SessionId)) { - ps = await (session.Batcher.PrepareCommandAsync(CommandType.Text, tsSelect, EmptyParams, cancellationToken)).ConfigureAwait(false); - rs = await (session.Batcher.ExecuteReaderAsync(ps, cancellationToken)).ConfigureAwait(false); - await (rs.ReadAsync(cancellationToken)).ConfigureAwait(false); - DateTime ts = rs.GetDateTime(0); - if (log.IsDebugEnabled) + try { - log.Debug("current timestamp retreived from db : " + ts + " (tiks=" + ts.Ticks + ")"); + ps = await (session.Batcher.PrepareCommandAsync(CommandType.Text, tsSelect, EmptyParams, cancellationToken)).ConfigureAwait(false); + rs = await (session.Batcher.ExecuteReaderAsync(ps, cancellationToken)).ConfigureAwait(false); + await (rs.ReadAsync(cancellationToken)).ConfigureAwait(false); + var ts = rs.GetDateTime(0); + log.DebugFormat("current timestamp retreived from db : {0} (ticks={1})", ts, ts.Ticks); + return ts; } - return ts; - } - catch (DbException sqle) - { - throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, sqle, - "could not select current db timestamp", tsSelect); - } - finally - { - if (ps != null) + catch (DbException sqle) { - try - { - session.Batcher.CloseCommand(ps, rs); - } - catch (DbException sqle) + throw ADOExceptionHelper.Convert( + session.Factory.SQLExceptionConverter, + sqle, + "could not select current db timestamp", + tsSelect); + } + finally + { + if (ps != null) { - log.Warn("unable to clean up prepared statement", sqle); + try + { + session.Batcher.CloseCommand(ps, rs); + } + catch (DbException sqle) + { + log.Warn("unable to clean up prepared statement", sqle); + } } } } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Type/OneToOneType.cs b/src/NHibernate/Async/Type/OneToOneType.cs index 32df395e268..524e2ac7200 100644 --- a/src/NHibernate/Async/Type/OneToOneType.cs +++ b/src/NHibernate/Async/Type/OneToOneType.cs @@ -164,4 +164,4 @@ public override Task AssembleAsync(object cached, ISessionImplementor se } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs b/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs index 7ab0ceb2386..185baa88eba 100644 --- a/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs +++ b/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs @@ -52,4 +52,4 @@ public virtual Task SeedAsync(ISessionImplementor session, CancellationT #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cfg/Configuration.cs b/src/NHibernate/Cfg/Configuration.cs index 34e0aeb4bd6..b209b6d201e 100644 --- a/src/NHibernate/Cfg/Configuration.cs +++ b/src/NHibernate/Cfg/Configuration.cs @@ -6,11 +6,11 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Serialization; using System.Security; using System.Text; using System.Xml; using System.Xml.Schema; -using System.Xml.Serialization; using NHibernate.Bytecode; using NHibernate.Cfg.ConfigurationSchema; @@ -29,7 +29,6 @@ using NHibernate.Type; using NHibernate.Util; using Array = System.Array; -using System.Runtime.Serialization; namespace NHibernate.Cfg { @@ -236,6 +235,9 @@ public bool HasNonIdentifierPropertyNamedId(string className) { return "id".Equals(GetIdentifierPropertyName(className)); } + + public Dialect.Dialect Dialect => + NHibernate.Dialect.Dialect.GetDialect(configuration.Properties); } private IMapping mapping; diff --git a/src/NHibernate/Cfg/Environment.cs b/src/NHibernate/Cfg/Environment.cs index 97c268f539f..f46e6674cb1 100644 --- a/src/NHibernate/Cfg/Environment.cs +++ b/src/NHibernate/Cfg/Environment.cs @@ -208,6 +208,12 @@ public static string Version /// public const string OdbcDateTimeScale = "odbc.explicit_datetime_scale"; + /// + /// Disable switching built-in NHibernate date-time types from DbType.DateTime to DbType.DateTime2 + /// for dialects supporting datetime2. + /// + public const string SqlTypesKeepDateTime = "sql_types.keep_datetime"; + /// /// Oracle has a dual Unicode support model. /// Either the whole database use an Unicode encoding, and then all string types diff --git a/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs b/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs index 4d9215ce474..4c18d55f26d 100644 --- a/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs +++ b/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs @@ -2,7 +2,7 @@ namespace NHibernate.Cfg.MappingSchema { /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -83,7 +83,7 @@ public HbmAny() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -110,7 +110,7 @@ public HbmMeta() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -128,7 +128,7 @@ public partial class HbmMetaValue { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -193,7 +193,7 @@ public partial class HbmColumn { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -207,7 +207,7 @@ public partial class HbmComment { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -366,7 +366,7 @@ public HbmArray() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -380,7 +380,7 @@ public partial class HbmSubselect { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -407,7 +407,7 @@ public HbmCache() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmCacheUsage { @@ -430,7 +430,7 @@ public enum HbmCacheUsage { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmCacheInclude { @@ -445,7 +445,7 @@ public enum HbmCacheInclude { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -459,7 +459,7 @@ public partial class HbmSynchronize { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -518,7 +518,7 @@ public HbmKey() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmOndelete { @@ -533,7 +533,7 @@ public enum HbmOndelete { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -559,7 +559,7 @@ public partial class HbmIndex { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -580,7 +580,7 @@ public partial class HbmListIndex { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -612,7 +612,7 @@ public partial class HbmCompositeElement { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -630,7 +630,7 @@ public partial class HbmParent { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -762,7 +762,7 @@ public HbmManyToOne() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -776,7 +776,7 @@ public partial class HbmFormula { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmOuterJoinStrategy { @@ -795,7 +795,7 @@ public enum HbmOuterJoinStrategy { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmFetchMode { @@ -810,7 +810,7 @@ public enum HbmFetchMode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmLaziness { @@ -829,7 +829,7 @@ public enum HbmLaziness { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmNotFoundMode { @@ -844,7 +844,7 @@ public enum HbmNotFoundMode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -880,7 +880,7 @@ public partial class HbmNestedCompositeElement { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -997,7 +997,7 @@ public HbmProperty() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1015,7 +1015,7 @@ public partial class HbmType { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1033,7 +1033,7 @@ public partial class HbmParam { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmPropertyGeneration { @@ -1052,7 +1052,7 @@ public enum HbmPropertyGeneration { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1113,7 +1113,7 @@ public HbmElement() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1143,7 +1143,7 @@ public partial class HbmManyToAny { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1241,7 +1241,7 @@ public HbmManyToMany() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1263,7 +1263,7 @@ public partial class HbmFilter { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmRestrictedLaziness { @@ -1278,7 +1278,7 @@ public enum HbmRestrictedLaziness { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1309,7 +1309,7 @@ public HbmOneToMany() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1323,7 +1323,7 @@ public partial class HbmLoader { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1353,7 +1353,7 @@ public partial class HbmCustomSQL { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmCustomSQLCheck { @@ -1372,7 +1372,7 @@ public enum HbmCustomSQLCheck { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmCollectionFetchMode { @@ -1391,7 +1391,7 @@ public enum HbmCollectionFetchMode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1565,7 +1565,7 @@ public HbmBag() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmCollectionLazy { @@ -1584,7 +1584,7 @@ public enum HbmCollectionLazy { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1627,7 +1627,6 @@ public partial class HbmClass { /// [System.Xml.Serialization.XmlElementAttribute("timestamp", typeof(HbmTimestamp))] - [System.Xml.Serialization.XmlElementAttribute("timestamputc", typeof(HbmTimestamputc))] [System.Xml.Serialization.XmlElementAttribute("version", typeof(HbmVersion))] public object Item1; @@ -1804,7 +1803,7 @@ public HbmClass() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1826,7 +1825,7 @@ public partial class HbmTuplizer { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmTuplizerEntitymode { @@ -1841,7 +1840,7 @@ public enum HbmTuplizerEntitymode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1891,7 +1890,7 @@ public HbmCompositeId() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1950,7 +1949,7 @@ public HbmKeyManyToOne() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -1995,7 +1994,7 @@ public partial class HbmKeyProperty { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmUnsavedValueType { @@ -2014,7 +2013,7 @@ public enum HbmUnsavedValueType { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2070,7 +2069,7 @@ public partial class HbmId { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2088,7 +2087,7 @@ public partial class HbmGenerator { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2142,7 +2141,7 @@ public HbmDiscriminator() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2169,7 +2168,7 @@ public HbmNaturalId() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2255,7 +2254,7 @@ public HbmComponent() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2319,7 +2318,7 @@ public HbmDynamicComponent() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2498,7 +2497,7 @@ public HbmList() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2686,7 +2685,7 @@ public HbmMap() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2705,7 +2704,7 @@ public partial class HbmCompositeIndex { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2724,7 +2723,7 @@ public partial class HbmCompositeMapKey { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2750,7 +2749,7 @@ public partial class HbmIndexManyToAny { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2780,7 +2779,7 @@ public partial class HbmIndexManyToMany { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2815,7 +2814,7 @@ public partial class HbmMapKey { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2850,7 +2849,7 @@ public partial class HbmMapKeyManyToMany { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -2937,7 +2936,7 @@ public HbmOneToOne() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3073,7 +3072,7 @@ public HbmPrimitiveArray() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmPrimitivearrayOuterjoin { @@ -3092,7 +3091,7 @@ public enum HbmPrimitivearrayOuterjoin { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmPrimitivearrayFetch { @@ -3111,7 +3110,7 @@ public enum HbmPrimitivearrayFetch { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3289,7 +3288,7 @@ public HbmSet() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3466,7 +3465,7 @@ public HbmIdbag() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3499,7 +3498,7 @@ public partial class HbmCollectionId { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3552,7 +3551,7 @@ public HbmTimestamp() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmTimestampUnsavedvalue { @@ -3567,7 +3566,7 @@ public enum HbmTimestampUnsavedvalue { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmTimestampSource { @@ -3582,7 +3581,7 @@ public enum HbmTimestampSource { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmVersionGeneration { @@ -3597,90 +3596,7 @@ public enum HbmVersionGeneration { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] - [System.Xml.Serialization.XmlRootAttribute("timestamputc", Namespace="urn:nhibernate-mapping-2.2", IsNullable=false)] - public partial class HbmTimestamputc { - - /// - [System.Xml.Serialization.XmlElementAttribute("meta")] - public HbmMeta[] meta; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string name; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string node; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string column; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string access; - - /// - [System.Xml.Serialization.XmlAttributeAttribute("unsaved-value")] - public HbmTimestamputcUnsavedvalue unsavedvalue; - - /// - [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool unsavedvalueSpecified; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - [System.ComponentModel.DefaultValueAttribute(HbmTimestamputcSource.Vm)] - public HbmTimestamputcSource source; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - [System.ComponentModel.DefaultValueAttribute(HbmVersionGeneration.Never)] - public HbmVersionGeneration generated; - - public HbmTimestamputc() { - this.source = HbmTimestamputcSource.Vm; - this.generated = HbmVersionGeneration.Never; - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] - [System.SerializableAttribute()] - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] - public enum HbmTimestamputcUnsavedvalue { - - /// - [System.Xml.Serialization.XmlEnumAttribute("null")] - Null, - - /// - [System.Xml.Serialization.XmlEnumAttribute("undefined")] - Undefined, - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] - [System.SerializableAttribute()] - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] - public enum HbmTimestamputcSource { - - /// - [System.Xml.Serialization.XmlEnumAttribute("vm")] - Vm, - - /// - [System.Xml.Serialization.XmlEnumAttribute("db")] - Db, - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3741,7 +3657,7 @@ public HbmVersion() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3793,7 +3709,7 @@ public HbmProperties() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -3876,7 +3792,7 @@ public HbmJoin() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:nhibernate-mapping-2.2")] public enum HbmJoinFetch { @@ -3891,7 +3807,7 @@ public enum HbmJoinFetch { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4060,7 +3976,7 @@ public HbmJoinedSubclass() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4081,7 +3997,7 @@ public partial class HbmResultSet { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4112,7 +4028,7 @@ public HbmLoadCollection() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4134,7 +4050,7 @@ public partial class HbmReturnProperty { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4148,7 +4064,7 @@ public partial class HbmReturnColumn { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmLockMode { @@ -4175,7 +4091,7 @@ public enum HbmLockMode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4214,7 +4130,7 @@ public HbmReturn() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4228,7 +4144,7 @@ public partial class HbmReturnDiscriminator { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4259,7 +4175,7 @@ public HbmReturnJoin() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4277,7 +4193,7 @@ public partial class HbmReturnScalar { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4352,7 +4268,7 @@ public HbmQuery() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4370,7 +4286,7 @@ public partial class HbmQueryParam { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmFlushMode { @@ -4393,7 +4309,7 @@ public enum HbmFlushMode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmCacheMode { @@ -4420,7 +4336,7 @@ public enum HbmCacheMode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4510,7 +4426,7 @@ public HbmSqlQuery() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4654,7 +4570,7 @@ public HbmSubclass() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4812,7 +4728,7 @@ public HbmUnionSubclass() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmPolymorphismType { @@ -4827,7 +4743,7 @@ public enum HbmPolymorphismType { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:nhibernate-mapping-2.2")] public enum HbmOptimisticLockMode { @@ -4850,7 +4766,7 @@ public enum HbmOptimisticLockMode { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4864,7 +4780,7 @@ public partial class HbmCreate { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4884,7 +4800,7 @@ public partial class HbmDatabaseObject { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4902,7 +4818,7 @@ public partial class HbmDefinition { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4916,7 +4832,7 @@ public partial class HbmDrop { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4934,7 +4850,7 @@ public partial class HbmDialectScope { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4969,7 +4885,7 @@ public HbmFilterDef() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -4987,7 +4903,7 @@ public partial class HbmFilterParam { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -5076,7 +4992,7 @@ public HbmMapping() { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -5098,7 +5014,7 @@ public partial class HbmTypedef { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0.Alpha1")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("HbmXsd", "5.0.0-Alpha1")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] diff --git a/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs index 0567c535abc..61092f74dac 100644 --- a/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs +++ b/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs @@ -114,13 +114,13 @@ private void BindTimestamp(HbmTimestamp timestampSchema, PersistentClass rootCla switch (timestampSchema.source) { case HbmTimestampSource.Vm: - simpleValue.TypeName = NHibernateUtil.Timestamp.Name; + simpleValue.TypeName = NHibernateUtil.DateTime.Name; break; case HbmTimestampSource.Db: - simpleValue.TypeName = NHibernateUtil.DbTimestamp.Name; + simpleValue.TypeName = NHibernateUtil.DbTimestamp.Name; break; default: - simpleValue.TypeName = NHibernateUtil.Timestamp.Name; + simpleValue.TypeName = NHibernateUtil.DateTime.Name; break; } } diff --git a/src/NHibernate/Dialect/DB2Dialect.cs b/src/NHibernate/Dialect/DB2Dialect.cs index f0b242ab89d..26d658f9140 100644 --- a/src/NHibernate/Dialect/DB2Dialect.cs +++ b/src/NHibernate/Dialect/DB2Dialect.cs @@ -99,8 +99,8 @@ public DB2Dialect() RegisterFunction("dayofyear", new StandardSQLFunction("dayofyear", NHibernateUtil.Int32)); RegisterFunction("days", new StandardSQLFunction("days", NHibernateUtil.Int32)); RegisterFunction("time", new StandardSQLFunction("time", NHibernateUtil.Time)); - RegisterFunction("timestamp", new StandardSQLFunction("timestamp", NHibernateUtil.Timestamp)); - RegisterFunction("timestamp_iso", new StandardSQLFunction("timestamp_iso", NHibernateUtil.Timestamp)); + RegisterFunction("timestamp", new StandardSQLFunction("timestamp", NHibernateUtil.DateTime)); + RegisterFunction("timestamp_iso", new StandardSQLFunction("timestamp_iso", NHibernateUtil.DateTime)); RegisterFunction("week", new StandardSQLFunction("week", NHibernateUtil.Int32)); RegisterFunction("week_iso", new StandardSQLFunction("week_iso", NHibernateUtil.Int32)); RegisterFunction("year", new StandardSQLFunction("year", NHibernateUtil.Int32)); @@ -285,4 +285,4 @@ public override string ForUpdateString #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs index 202923418c5..a85a330b4c9 100644 --- a/src/NHibernate/Dialect/Dialect.cs +++ b/src/NHibernate/Dialect/Dialect.cs @@ -135,7 +135,7 @@ protected Dialect() RegisterHibernateType(DbType.Int16, NHibernateUtil.Int16.Name); RegisterHibernateType(DbType.SByte, NHibernateUtil.SByte.Name); RegisterHibernateType(DbType.Time, NHibernateUtil.Time.Name); - RegisterHibernateType(DbType.DateTime, NHibernateUtil.Timestamp.Name); + RegisterHibernateType(DbType.DateTime, NHibernateUtil.DateTime.Name); RegisterHibernateType(DbType.String, NHibernateUtil.String.Name); RegisterHibernateType(DbType.VarNumeric, NHibernateUtil.Decimal.Name); RegisterHibernateType(DbType.Decimal, NHibernateUtil.Decimal.Name); @@ -213,7 +213,7 @@ public virtual void Configure(IDictionary settings) /// The database type name used by ddl. public virtual string GetTypeName(SqlType sqlType) { - if (sqlType.LengthDefined || sqlType.PrecisionDefined) + if (sqlType.LengthDefined || sqlType.PrecisionDefined || sqlType.ScaleDefined) { string resultWithLength = _typeNames.Get(sqlType.DbType, sqlType.Length, sqlType.Precision, sqlType.Scale); if (resultWithLength != null) return resultWithLength; @@ -274,7 +274,7 @@ public virtual string GetCastTypeName(SqlType sqlType) /// length (if appropriate) /// /// The typecode - /// Maximum length of database type + /// Maximum length or scale of database type /// The database type name protected void RegisterColumnType(DbType code, int capacity, string name) { @@ -292,6 +292,14 @@ protected void RegisterColumnType(DbType code, string name) _typeNames.Put(code, name); } + /// + /// Override provided s. + /// + /// The original . + /// Refined s. + public virtual SqlType OverrideSqlType(SqlType type) => + type; + #endregion #region DDL support @@ -2123,6 +2131,11 @@ public virtual bool SupportsSubSelects /// public virtual bool SupportsDistributedTransactions => true; + /// + /// Does this dialect handles date and time types scale (fractional seconds precision)? + /// + public virtual bool SupportsDateTimeScale => false; + #endregion /// diff --git a/src/NHibernate/Dialect/InformixDialect.cs b/src/NHibernate/Dialect/InformixDialect.cs index 7d5c2ffa196..4ab647b632b 100644 --- a/src/NHibernate/Dialect/InformixDialect.cs +++ b/src/NHibernate/Dialect/InformixDialect.cs @@ -84,7 +84,7 @@ public InformixDialect() RegisterFunction("date", new StandardSQLFunction("date", NHibernateUtil.DateTime)); RegisterFunction("mdy", new SQLFunctionTemplate(NHibernateUtil.DateTime, "mdy(?1, ?2, ?3)")); RegisterFunction("to_char", new StandardSQLFunction("to_char", NHibernateUtil.String)); - RegisterFunction("to_date", new StandardSQLFunction("to_date", NHibernateUtil.Timestamp)); + RegisterFunction("to_date", new StandardSQLFunction("to_date", NHibernateUtil.DateTime)); RegisterFunction("instr", new StandardSQLFunction("instr", NHibernateUtil.String)); // actually there is no Instr (or equivalent) in Informix; you have to write your own SPL or UDR @@ -496,4 +496,4 @@ public override string ExtractConstraintName(DbException sqle) return constraintName; } } ; -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/MsSql2000Dialect.cs b/src/NHibernate/Dialect/MsSql2000Dialect.cs index 3bd6d58fa14..0e86557107b 100644 --- a/src/NHibernate/Dialect/MsSql2000Dialect.cs +++ b/src/NHibernate/Dialect/MsSql2000Dialect.cs @@ -410,10 +410,8 @@ public override string CurrentTimestampSQLFunctionName get { return "CURRENT_TIMESTAMP"; } } - public override string CurrentTimestampSelectString - { - get { return "SELECT CURRENT_TIMESTAMP"; } - } + public override string CurrentTimestampSelectString => + "SELECT " + CurrentTimestampSQLFunctionName; public override bool IsCurrentTimestampSelectStringCallable { diff --git a/src/NHibernate/Dialect/MsSql2008Dialect.cs b/src/NHibernate/Dialect/MsSql2008Dialect.cs index 6da8c5a144b..d19bfc7b19c 100644 --- a/src/NHibernate/Dialect/MsSql2008Dialect.cs +++ b/src/NHibernate/Dialect/MsSql2008Dialect.cs @@ -1,26 +1,61 @@ -using System.Data; -using NHibernate.Cfg; +using System.Collections.Generic; +using System.Data; using NHibernate.Dialect.Function; using NHibernate.Driver; +using NHibernate.SqlTypes; +using NHibernate.Util; +using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Dialect { public class MsSql2008Dialect : MsSql2005Dialect { + /// + /// Should be preserved instead of switching it to ? + /// + /// + /// for preserving , for + /// replacing it with . + /// + protected bool KeepDateTime { get; private set; } + + public override void Configure(IDictionary settings) + { + base.Configure(settings); + + KeepDateTime = PropertiesHelper.GetBoolean(Environment.SqlTypesKeepDateTime, settings, false); + if (KeepDateTime) + { + // Re-register functions, they depend on this setting. + RegisterFunctions(); + } + } + protected override void RegisterDateTimeTypeMappings() { base.RegisterDateTimeTypeMappings(); + // Not overriding default scale: it is already the max, 7. RegisterColumnType(DbType.DateTime2, "DATETIME2"); + RegisterColumnType(DbType.DateTime2, 7, "DATETIME2($s)"); RegisterColumnType(DbType.DateTimeOffset, "DATETIMEOFFSET"); + RegisterColumnType(DbType.DateTimeOffset, 7, "DATETIMEOFFSET($s)"); RegisterColumnType(DbType.Date, "DATE"); RegisterColumnType(DbType.Time, "TIME"); + RegisterColumnType(DbType.Time, 7, "TIME($s)"); } protected override void RegisterFunctions() { base.RegisterFunctions(); - RegisterFunction("current_timestamp", new NoArgSQLFunction("sysdatetime", NHibernateUtil.DateTime2, true)); - RegisterFunction("current_timestamp_offset", new NoArgSQLFunction("sysdatetimeoffset", NHibernateUtil.DateTimeOffset, true)); + if (!KeepDateTime) + { + RegisterFunction( + "current_timestamp", + new NoArgSQLFunction("sysdatetime", NHibernateUtil.DateTime, true)); + } + RegisterFunction( + "current_timestamp_offset", + new NoArgSQLFunction("sysdatetimeoffset", NHibernateUtil.DateTimeOffset, true)); } protected override void RegisterKeywords() @@ -36,5 +71,28 @@ protected override void RegisterDefaultProperties() base.RegisterDefaultProperties(); DefaultProperties[Environment.ConnectionDriver] = typeof(Sql2008ClientDriver).AssemblyQualifiedName; } + + public override string CurrentTimestampSQLFunctionName => + KeepDateTime ? base.CurrentTimestampSQLFunctionName : "SYSDATETIME()"; + + public override long TimestampResolutionInTicks => + KeepDateTime + ? base.TimestampResolutionInTicks + // MS SQL resolution with datetime2 is 100ns, one tick. + : 1; + + /// + public override SqlType OverrideSqlType(SqlType type) + { + type = base.OverrideSqlType(type); + return !KeepDateTime && type is DateTimeSqlType dateTimeType + ? dateTimeType.ScaleDefined + ? SqlTypeFactory.GetDateTime2(dateTimeType.Scale) + : SqlTypeFactory.DateTime2 + : type; + } + + /// + public override bool SupportsDateTimeScale => !KeepDateTime; } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/MySQL57Dialect.cs b/src/NHibernate/Dialect/MySQL57Dialect.cs new file mode 100644 index 00000000000..8c06d81ee7f --- /dev/null +++ b/src/NHibernate/Dialect/MySQL57Dialect.cs @@ -0,0 +1,20 @@ +using System.Data; + +namespace NHibernate.Dialect +{ + public class MySQL57Dialect : MySQL55Dialect + { + public MySQL57Dialect() + { + RegisterColumnType(DbType.DateTime, "DATETIME(6)"); + RegisterColumnType(DbType.DateTime, 6, "DATETIME($s)"); + RegisterColumnType(DbType.Time, "TIME(6)"); + RegisterColumnType(DbType.Time, 6, "TIME($s)"); + } + + public override long TimestampResolutionInTicks => 10; + + /// + public override bool SupportsDateTimeScale => true; + } +} diff --git a/src/NHibernate/Dialect/Oracle8iDialect.cs b/src/NHibernate/Dialect/Oracle8iDialect.cs index 2400b070595..fb20ded0cf4 100644 --- a/src/NHibernate/Dialect/Oracle8iDialect.cs +++ b/src/NHibernate/Dialect/Oracle8iDialect.cs @@ -244,7 +244,7 @@ protected virtual void RegisterFunctions() RegisterFunction("right", new SQLFunctionTemplate(NHibernateUtil.String, "substr(?1, -?2)")); RegisterFunction("to_char", new StandardSQLFunction("to_char", NHibernateUtil.String)); - RegisterFunction("to_date", new StandardSQLFunction("to_date", NHibernateUtil.Timestamp)); + RegisterFunction("to_date", new StandardSQLFunction("to_date", NHibernateUtil.DateTime)); RegisterFunction("current_date", new NoArgSQLFunction("current_date", NHibernateUtil.Date, false)); RegisterFunction("current_time", new NoArgSQLFunction("current_timestamp", NHibernateUtil.Time, false)); @@ -260,7 +260,7 @@ protected virtual void RegisterFunctions() RegisterFunction("last_day", new StandardSQLFunction("last_day", NHibernateUtil.Date)); RegisterFunction("sysdate", new NoArgSQLFunction("sysdate", NHibernateUtil.Date, false)); - RegisterFunction("systimestamp", new NoArgSQLFunction("systimestamp", NHibernateUtil.Timestamp, false)); + RegisterFunction("systimestamp", new NoArgSQLFunction("systimestamp", NHibernateUtil.DateTime, false)); RegisterFunction("uid", new NoArgSQLFunction("uid", NHibernateUtil.Int32, false)); RegisterFunction("user", new NoArgSQLFunction("user", NHibernateUtil.String, false)); @@ -610,4 +610,4 @@ public SqlString Render(IList args, ISessionFactoryImplementor factory) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Oracle9iDialect.cs b/src/NHibernate/Dialect/Oracle9iDialect.cs index 2086e03397e..3008e340e6e 100644 --- a/src/NHibernate/Dialect/Oracle9iDialect.cs +++ b/src/NHibernate/Dialect/Oracle9iDialect.cs @@ -23,16 +23,14 @@ public override string CurrentTimestampSQLFunctionName protected override void RegisterDateTimeTypeMappings() { RegisterColumnType(DbType.Date, "DATE"); - RegisterColumnType(DbType.DateTime, "TIMESTAMP(4)"); - RegisterColumnType(DbType.Time, "TIMESTAMP(4)"); + RegisterColumnType(DbType.DateTime, "TIMESTAMP(7)"); + RegisterColumnType(DbType.DateTime, 9, "TIMESTAMP($s)"); + RegisterColumnType(DbType.Time, "TIMESTAMP(7)"); + RegisterColumnType(DbType.Time, 9, "TIMESTAMP($s)"); RegisterColumnType(DbType.Xml, "XMLTYPE"); } - public override long TimestampResolutionInTicks - { - // matches precision of TIMESTAMP(4) - get { return 1000L; } - } + public override long TimestampResolutionInTicks => 1; public override string GetSelectClauseNullString(SqlType sqlType) { @@ -44,5 +42,8 @@ public override CaseFragment CreateCaseFragment() // Oracle did add support for ANSI CASE statements in 9i return new ANSICaseFragment(this); } + + /// + public override bool SupportsDateTimeScale => true; } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/OracleLiteDialect.cs b/src/NHibernate/Dialect/OracleLiteDialect.cs index 6408b7a4e4d..707736fb166 100644 --- a/src/NHibernate/Dialect/OracleLiteDialect.cs +++ b/src/NHibernate/Dialect/OracleLiteDialect.cs @@ -80,7 +80,7 @@ public OracleLiteDialect() RegisterFunction("length", new StandardSQLFunction("length", NHibernateUtil.Int64)); RegisterFunction("to_char", new StandardSQLFunction("to_char", NHibernateUtil.String)); - RegisterFunction("to_date", new StandardSQLFunction("to_date", NHibernateUtil.Timestamp)); + RegisterFunction("to_date", new StandardSQLFunction("to_date", NHibernateUtil.DateTime)); RegisterFunction("last_day", new StandardSQLFunction("last_day", NHibernateUtil.Date)); RegisterFunction("sysdate", new NoArgSQLFunction("sysdate", NHibernateUtil.Date, false)); @@ -118,4 +118,4 @@ protected override string GetCreateSequenceString(string sequenceName, int initi return GetCreateSequenceString(sequenceName) + " increment by " + incrementSize + " start with " + initialValue; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/PostgreSQL81Dialect.cs b/src/NHibernate/Dialect/PostgreSQL81Dialect.cs index e08e8dd526f..05c9592d339 100644 --- a/src/NHibernate/Dialect/PostgreSQL81Dialect.cs +++ b/src/NHibernate/Dialect/PostgreSQL81Dialect.cs @@ -1,4 +1,3 @@ -using System; using System.Data; using NHibernate.SqlCommand; @@ -34,6 +33,15 @@ namespace NHibernate.Dialect /// public class PostgreSQL81Dialect : PostgreSQLDialect { + protected override void RegisterDateTimeTypeMappings() + { + base.RegisterDateTimeTypeMappings(); + RegisterColumnType(DbType.DateTime, 6, "timestamp($s)"); + RegisterColumnType(DbType.Time, 6, "time($s)"); + // Not overriding default scale: Posgres doc writes it means "no explicit limit", so max of what it can support, + // which suits our needs. + } + public override string ForUpdateNowaitString { get { return " for update nowait"; } @@ -105,5 +113,8 @@ public override bool SupportsInsertSelectIdentity { get { return true; } } + + /// + public override bool SupportsDateTimeScale => true; } } diff --git a/src/NHibernate/Dialect/PostgreSQLDialect.cs b/src/NHibernate/Dialect/PostgreSQLDialect.cs index f33ce63a5d6..f45ff7040de 100644 --- a/src/NHibernate/Dialect/PostgreSQLDialect.cs +++ b/src/NHibernate/Dialect/PostgreSQLDialect.cs @@ -29,7 +29,8 @@ public class PostgreSQLDialect : Dialect public PostgreSQLDialect() { DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.NpgsqlDriver"; - + + RegisterDateTimeTypeMappings(); RegisterColumnType(DbType.AnsiStringFixedLength, "char(255)"); RegisterColumnType(DbType.AnsiStringFixedLength, 8000, "char($l)"); RegisterColumnType(DbType.AnsiString, "varchar(255)"); @@ -40,8 +41,6 @@ public PostgreSQLDialect() RegisterColumnType(DbType.Boolean, "boolean"); RegisterColumnType(DbType.Byte, "int2"); RegisterColumnType(DbType.Currency, "decimal(16,4)"); - RegisterColumnType(DbType.Date, "date"); - RegisterColumnType(DbType.DateTime, "timestamp"); RegisterColumnType(DbType.Decimal, "decimal(19,5)"); RegisterColumnType(DbType.Decimal, 19, "decimal($p, $s)"); RegisterColumnType(DbType.Double, "float8"); @@ -54,7 +53,6 @@ public PostgreSQLDialect() RegisterColumnType(DbType.String, "varchar(255)"); RegisterColumnType(DbType.String, 4000, "varchar($l)"); RegisterColumnType(DbType.String, 1073741823, "text"); - RegisterColumnType(DbType.Time, "time"); // Override standard HQL function RegisterFunction("current_timestamp", new NoArgSQLFunction("now", NHibernateUtil.DateTime, true)); @@ -114,6 +112,13 @@ public PostgreSQLDialect() #endregion + protected virtual void RegisterDateTimeTypeMappings() + { + RegisterColumnType(DbType.Date, "date"); + RegisterColumnType(DbType.DateTime, "timestamp"); + RegisterColumnType(DbType.Time, "time"); + } + protected virtual void RegisterKeywords() { RegisterKeywords(DialectKeywords); diff --git a/src/NHibernate/Dialect/SybaseASE15Dialect.cs b/src/NHibernate/Dialect/SybaseASE15Dialect.cs index 70d3da38d58..a1b1be5e49d 100644 --- a/src/NHibernate/Dialect/SybaseASE15Dialect.cs +++ b/src/NHibernate/Dialect/SybaseASE15Dialect.cs @@ -67,15 +67,15 @@ public SybaseASE15Dialect() RegisterFunction("cot", new StandardSQLFunction("cot", NHibernateUtil.Double)); RegisterFunction("current_date", new NoArgSQLFunction("getdate", NHibernateUtil.Date)); RegisterFunction("current_time", new NoArgSQLFunction("getdate", NHibernateUtil.Time)); - RegisterFunction("current_timestamp", new NoArgSQLFunction("getdate", NHibernateUtil.Timestamp)); + RegisterFunction("current_timestamp", new NoArgSQLFunction("getdate", NHibernateUtil.DateTime)); RegisterFunction("datename", new StandardSQLFunction("datename", NHibernateUtil.String)); RegisterFunction("day", new StandardSQLFunction("day", NHibernateUtil.Int32)); RegisterFunction("degrees", new StandardSQLFunction("degrees", NHibernateUtil.Double)); RegisterFunction("exp", new StandardSQLFunction("exp", NHibernateUtil.Double)); RegisterFunction("extract", new SQLFunctionTemplate(NHibernateUtil.Int32, "datepart(?1, ?3)")); RegisterFunction("floor", new StandardSQLFunction("floor")); - RegisterFunction("getdate", new NoArgSQLFunction("getdate", NHibernateUtil.Timestamp)); - RegisterFunction("getutcdate", new NoArgSQLFunction("getutcdate", NHibernateUtil.Timestamp)); + RegisterFunction("getdate", new NoArgSQLFunction("getdate", NHibernateUtil.DateTime)); + RegisterFunction("getutcdate", new NoArgSQLFunction("getutcdate", NHibernateUtil.DateTime)); RegisterFunction("hour", new SQLFunctionTemplate(NHibernateUtil.Int32, "datepart(hour, ?1)")); RegisterFunction("isnull", new StandardSQLFunction("isnull")); RegisterFunction("len", new StandardSQLFunction("len", NHibernateUtil.Int64)); @@ -330,4 +330,4 @@ public override DbDataReader GetResultSet(DbCommand statement) return statement.ExecuteReader(); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs index 46b524356fe..a45cfda6775 100644 --- a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs +++ b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs @@ -199,17 +199,17 @@ protected virtual void RegisterBitFunctions() protected virtual void RegisterDateFunctions() { RegisterFunction("date", new StandardSQLFunction("date", NHibernateUtil.Date)); - RegisterFunction("dateadd", new StandardSQLFunction("dateadd", NHibernateUtil.Timestamp)); + RegisterFunction("dateadd", new StandardSQLFunction("dateadd", NHibernateUtil.DateTime)); RegisterFunction("datediff", new StandardSQLFunction("datediff", NHibernateUtil.Int32)); RegisterFunction("dateformat", new StandardSQLFunction("dateformat", NHibernateUtil.String)); RegisterFunction("datename", new StandardSQLFunction("datename", NHibernateUtil.String)); RegisterFunction("datepart", new StandardSQLFunction("datepart", NHibernateUtil.Int32)); - RegisterFunction("datetime", new StandardSQLFunction("datetime", NHibernateUtil.Timestamp)); + RegisterFunction("datetime", new StandardSQLFunction("datetime", NHibernateUtil.DateTime)); RegisterFunction("day", new StandardSQLFunction("day", NHibernateUtil.Int32)); RegisterFunction("dayname", new StandardSQLFunction("dayname", NHibernateUtil.String)); RegisterFunction("days", new StandardSQLFunction("days")); RegisterFunction("dow", new StandardSQLFunction("dow", NHibernateUtil.Int32)); - RegisterFunction("getdate", new StandardSQLFunction("getdate", NHibernateUtil.Timestamp)); + RegisterFunction("getdate", new StandardSQLFunction("getdate", NHibernateUtil.DateTime)); RegisterFunction("hour", new StandardSQLFunction("hour", NHibernateUtil.Int32)); RegisterFunction("hours", new StandardSQLFunction("hours")); RegisterFunction("minute", new StandardSQLFunction("minute", NHibernateUtil.Int32)); @@ -217,7 +217,7 @@ protected virtual void RegisterDateFunctions() RegisterFunction("month", new StandardSQLFunction("month", NHibernateUtil.Int32)); RegisterFunction("monthname", new StandardSQLFunction("monthname", NHibernateUtil.String)); RegisterFunction("months", new StandardSQLFunction("months")); - RegisterFunction("now", new NoArgSQLFunction("now", NHibernateUtil.Timestamp)); + RegisterFunction("now", new NoArgSQLFunction("now", NHibernateUtil.DateTime)); RegisterFunction("quarter", new StandardSQLFunction("quarter", NHibernateUtil.Int32)); RegisterFunction("second", new StandardSQLFunction("second", NHibernateUtil.Int32)); RegisterFunction("seconds", new StandardSQLFunction("seconds")); @@ -228,7 +228,7 @@ protected virtual void RegisterDateFunctions() RegisterFunction("ymd", new StandardSQLFunction("ymd", NHibernateUtil.Date)); // compatibility functions - RegisterFunction("current_timestamp", new NoArgSQLFunction("getdate", NHibernateUtil.Timestamp, true)); + RegisterFunction("current_timestamp", new NoArgSQLFunction("getdate", NHibernateUtil.DateTime, true)); RegisterFunction("current_time", new NoArgSQLFunction("getdate", NHibernateUtil.Time, true)); RegisterFunction("current_date", new NoArgSQLFunction("getdate", NHibernateUtil.Date, true)); } @@ -929,4 +929,4 @@ public override IDataBaseSchema GetDataBaseSchema(DbConnection connection) return new SybaseAnywhereDataBaseMetaData(connection); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/TypeNames.cs b/src/NHibernate/Dialect/TypeNames.cs index 57a9f0108f8..4ceb8a49a88 100644 --- a/src/NHibernate/Dialect/TypeNames.cs +++ b/src/NHibernate/Dialect/TypeNames.cs @@ -72,44 +72,63 @@ public string Get(DbType typecode) /// the SQL scale /// the SQL precision /// - /// The associated name with smallest capacity >= size if available and the - /// default type name otherwise + /// The associated name with smallest capacity >= size (or scale for date time types) if available, + /// otherwise the default type name. /// public string Get(DbType typecode, int size, int precision, int scale) { - SortedList map; - weighted.TryGetValue(typecode, out map); + weighted.TryGetValue(typecode, out var map); if (map != null && map.Count > 0) { - foreach (KeyValuePair entry in map) + var requiredCapacity = IsScaleType(typecode) ? scale : size; + foreach (var entry in map) { - if (size <= entry.Key) + if (requiredCapacity <= entry.Key) { return Replace(entry.Value, size, precision, scale); } } } - //Could not find a specific type for the size, using the default + //Could not find a specific type for the capacity, using the default return Replace(Get(typecode), size, precision, scale); } /// - /// For types with a simple length, this method returns the definition + /// For types with a simple length (or scale for date time types), this method returns the definition /// for the longest registered type. /// /// /// public string GetLongest(DbType typecode) { - SortedList map; - weighted.TryGetValue(typecode, out map); - + weighted.TryGetValue(typecode, out var map); if (map != null && map.Count > 0) - return Replace(map.Values[map.Count - 1], map.Keys[map.Count - 1], 0, 0); + { + var isScaleType = IsScaleType(typecode); + var capacity = map.Keys[map.Count - 1]; + return Replace( + map.Values[map.Count - 1], + isScaleType ? 0 : capacity, + 0, + isScaleType ? capacity : 0); + } return Get(typecode); } + private static bool IsScaleType(DbType typecode) + { + switch (typecode) + { + case DbType.DateTime: + case DbType.DateTime2: + case DbType.DateTimeOffset: + case DbType.Time: + return true; + } + return false; + } + private static string Replace(string type, int size, int precision, int scale) { type = StringHelper.ReplaceOnce(type, LengthPlaceHolder, size.ToString()); @@ -121,7 +140,7 @@ private static string Replace(string type, int size, int precision, int scale) /// Set a type name for specified type key and capacity /// /// the type key - /// the (maximum) type size/length + /// the (maximum) type size/length or scale /// The associated name public void Put(DbType typecode, int capacity, string value) { @@ -144,4 +163,4 @@ public void Put(DbType typecode, string value) defaults[typecode] = value; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Driver/DriverBase.cs b/src/NHibernate/Driver/DriverBase.cs index 46601c7b916..efc8629f1a2 100644 --- a/src/NHibernate/Driver/DriverBase.cs +++ b/src/NHibernate/Driver/DriverBase.cs @@ -231,7 +231,7 @@ public void RemoveUnusedCommandParameters(DbCommand cmd, SqlString sqlString) .ForEach(unusedParameterName => cmd.Parameters.RemoveAt(unusedParameterName)); } - public virtual void ExpandQueryParameters(DbCommand cmd, SqlString sqlString) + public virtual void ExpandQueryParameters(DbCommand cmd, SqlString sqlString, SqlType[] parameterTypes) { if (UseNamedPrefixInSql) return; // named parameters are ok @@ -242,8 +242,10 @@ public virtual void ExpandQueryParameters(DbCommand cmd, SqlString sqlString) var parameter = part as Parameter; if (parameter != null) { - var originalParameter = cmd.Parameters[parameter.ParameterPosition.Value]; - expandedParameters.Add(CloneParameter(cmd, originalParameter)); + var index = parameter.ParameterPosition.Value; + var originalParameter = cmd.Parameters[index]; + var originalType = parameterTypes[index]; + expandedParameters.Add(CloneParameter(cmd, originalParameter, originalType)); } } @@ -262,11 +264,9 @@ public virtual bool SupportsMultipleQueries get { return false; } } - protected virtual DbParameter CloneParameter(DbCommand cmd, DbParameter originalParameter) + protected virtual DbParameter CloneParameter(DbCommand cmd, DbParameter originalParameter, SqlType originalType) { - var clone = cmd.CreateParameter(); - clone.DbType = originalParameter.DbType; - clone.ParameterName = originalParameter.ParameterName; + var clone = GenerateParameter(cmd, originalParameter.ParameterName, originalType); clone.Value = originalParameter.Value; return clone; } diff --git a/src/NHibernate/Driver/IDriver.cs b/src/NHibernate/Driver/IDriver.cs index 6420d052fdd..61e40b64213 100644 --- a/src/NHibernate/Driver/IDriver.cs +++ b/src/NHibernate/Driver/IDriver.cs @@ -105,7 +105,7 @@ public interface IDriver /// for 'select ... from MyTable t where t.Col1 = @p0 and t.Col2 = @p0' we can issue /// 'select ... from MyTable t where t.Col1 = ? and t.Col2 = ?' /// - void ExpandQueryParameters(DbCommand cmd, SqlString sqlString); + void ExpandQueryParameters(DbCommand cmd, SqlString sqlString, SqlType[] parameterTypes); IResultSetsCommand GetResultSetsCommand(ISessionImplementor session); bool SupportsMultipleQueries { get; } diff --git a/src/NHibernate/Driver/OdbcDriver.cs b/src/NHibernate/Driver/OdbcDriver.cs index 0922822754f..7bf904daf75 100644 --- a/src/NHibernate/Driver/OdbcDriver.cs +++ b/src/NHibernate/Driver/OdbcDriver.cs @@ -61,19 +61,36 @@ public override string NamedPrefix private void SetVariableLengthParameterSize(DbParameter dbParam, SqlType sqlType) { - if (Equals(sqlType, SqlTypeFactory.DateTime) && _dbDateTimeScale != null) - ((IDbDataParameter)dbParam).Scale = _dbDateTimeScale.Value; + if (sqlType is DateTimeSqlType && _dbDateTimeScale != null) + dbParam.Scale = _dbDateTimeScale.Value; - // Override the defaults using data from SqlType. if (sqlType.LengthDefined) { - dbParam.Size = sqlType.Length; + switch (dbParam.DbType) + { + case DbType.AnsiString: + case DbType.AnsiStringFixedLength: + case DbType.String: + case DbType.StringFixedLength: + // NH-4083: do not limit to column length if above 2000. Setting size may trigger conversion from + // nvarchar to ntext when size is superior or equal to 2000, causing some queries to fail: + // https://stackoverflow.com/q/8569844/1178314 + // So we cannot do as the SqlServerClientDriver which set max default length instead. + // This may also cause NH-3895, forbidding like comparisons which may need + // some more length. + // Moreover specifying size is a SQL Server optimization for query + // plan cache, but we have no knowledge here if the target database will be SQL-Server. + break; + default: + dbParam.Size = sqlType.Length; + break; + } } if (sqlType.PrecisionDefined) { - ((IDbDataParameter)dbParam).Precision = sqlType.Precision; - ((IDbDataParameter)dbParam).Scale = sqlType.Scale; + dbParam.Precision = sqlType.Precision; + dbParam.Scale = sqlType.Scale; } } diff --git a/src/NHibernate/Driver/SqlClientDriver.cs b/src/NHibernate/Driver/SqlClientDriver.cs index 8a01a2f03db..beb0c951574 100644 --- a/src/NHibernate/Driver/SqlClientDriver.cs +++ b/src/NHibernate/Driver/SqlClientDriver.cs @@ -114,8 +114,8 @@ public static void SetVariableLengthParameterSize(DbParameter dbParam, SqlType s if (sqlType.PrecisionDefined) { - ((IDbDataParameter) dbParam).Precision = sqlType.Precision; - ((IDbDataParameter) dbParam).Scale = sqlType.Scale; + dbParam.Precision = sqlType.Precision; + dbParam.Scale = sqlType.Scale; } } @@ -131,8 +131,8 @@ protected static void SetDefaultParameterSize(DbParameter dbParam, SqlType sqlTy dbParam.Size = IsBlob(dbParam, sqlType) ? MaxSizeForBlob : MaxSizeForLengthLimitedBinary; break; case DbType.Decimal: - ((IDbDataParameter) dbParam).Precision = MaxPrecision; - ((IDbDataParameter) dbParam).Scale = MaxScale; + dbParam.Precision = MaxPrecision; + dbParam.Scale = MaxScale; break; case DbType.String: case DbType.StringFixedLength: diff --git a/src/NHibernate/Engine/IMapping.cs b/src/NHibernate/Engine/IMapping.cs index 92eb0492360..2fe745e6eb9 100644 --- a/src/NHibernate/Engine/IMapping.cs +++ b/src/NHibernate/Engine/IMapping.cs @@ -15,5 +15,10 @@ public interface IMapping IType GetReferencedPropertyType(string className, string propertyName); bool HasNonIdentifierPropertyNamedId(string className); + + /// + /// The current . + /// + Dialect.Dialect Dialect { get; } } } diff --git a/src/NHibernate/Engine/ISessionFactoryImplementor.cs b/src/NHibernate/Engine/ISessionFactoryImplementor.cs index becaef770de..0f3d9972ecd 100644 --- a/src/NHibernate/Engine/ISessionFactoryImplementor.cs +++ b/src/NHibernate/Engine/ISessionFactoryImplementor.cs @@ -24,11 +24,6 @@ namespace NHibernate.Engine /// public interface ISessionFactoryImplementor : IMapping, ISessionFactory { - /// - /// Get the SQL . - /// - Dialect.Dialect Dialect { get; } - IInterceptor Interceptor { get; } QueryPlanCache QueryPlanCache { get; } @@ -181,4 +176,4 @@ ISession OpenSession(DbConnection connection, bool flushBeforeCompletionEnabled, string TryGetGuessEntityName(System.Type implementor); #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs index e6131efd52b..e2c66a91f2d 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs @@ -362,7 +362,7 @@ private static bool IsDatabaseGeneratedTimestamp(IType type) { // TODO NH: we should check the "generated" property // currently only the Hibernate-supplied DbTimestampType is supported here - return type is TimestampType; + return type is DbTimestampType; } private static bool IsIntegral(IType type) diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/IdentNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/IdentNode.cs index 5fb34d8fa55..a4b4f5607c3 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/IdentNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/IdentNode.cs @@ -39,7 +39,7 @@ public override IType DataType return fe.DataType; } ISQLFunction sf = Walker.SessionFactoryHelper.FindSQLFunction(Text); - return sf == null ? null : sf.ReturnType(null, null); + return sf?.ReturnType(null, Walker.SessionFactoryHelper.Factory); } set diff --git a/src/NHibernate/IDetachedQuery.cs b/src/NHibernate/IDetachedQuery.cs index b3006619d33..644532a29b9 100644 --- a/src/NHibernate/IDetachedQuery.cs +++ b/src/NHibernate/IDetachedQuery.cs @@ -213,6 +213,8 @@ public interface IDetachedQuery /// /// The position of the parameter in the query string, numbered from 0 /// A non-null instance of a . + /// Since v5.0, does no more cut milliseconds. Use + /// for this IDetachedQuery SetDateTime(int position, DateTime val); /// @@ -221,8 +223,26 @@ public interface IDetachedQuery /// /// A non-null instance of a . /// The name of the parameter + /// Since v5.0, does no more cut milliseconds. Use + /// for this IDetachedQuery SetDateTime(string name, DateTime val); + /// + /// Bind an instance of a to an indexed parameter + /// using an NHibernate . + /// + /// The position of the parameter in the query string, numbered from 0 + /// A non-null instance of a . + IDetachedQuery SetDateTimeNoMs(int position, DateTime val); + + /// + /// Bind an instance of a to a named parameter + /// using an NHibernate . + /// + /// A non-null instance of a . + /// The name of the parameter + IDetachedQuery SetDateTimeNoMs(string name, DateTime val); + /// /// Bind an instance of a to an indexed parameter /// using an NHibernate . @@ -381,20 +401,24 @@ public interface IDetachedQuery /// A non-null instance of a . IDetachedQuery SetTime(string name, DateTime val); + // Obsolete since v5.0 /// /// Bind an instance of a to an indexed parameter /// using an NHibernate . /// /// The position of the parameter in the query string, numbered from 0 /// A non-null instance of a . + [Obsolete("Use SetDateTime instead.")] IDetachedQuery SetTimestamp(int position, DateTime val); + // Obsolete since v5.0 /// /// Bind an instance of a to a named parameter /// using an NHibernate . /// /// The name of the parameter /// A non-null instance of a . + [Obsolete("Use SetDateTime instead.")] IDetachedQuery SetTimestamp(string name, DateTime val); /// diff --git a/src/NHibernate/IMultiQuery.cs b/src/NHibernate/IMultiQuery.cs index 84bf43f4c0d..89588573d37 100644 --- a/src/NHibernate/IMultiQuery.cs +++ b/src/NHibernate/IMultiQuery.cs @@ -241,8 +241,21 @@ public partial interface IMultiQuery /// A non-null instance of a . /// The name of the parameter /// The instance for method chain. + /// Since v5.0, does no more cut milliseconds. Use + /// for this IMultiQuery SetDateTime(string name, DateTime val); + /// + /// Bind an instance of a to a named parameter + /// using an NHibernate . + /// + /// A non-null instance of a . + /// The name of the parameter + /// The instance for method chain. + IMultiQuery SetDateTimeNoMs(string name, DateTime val); + + // Since v5.0 + [Obsolete("Use SetDateTime instead, it uses DateTime2 with dialects supporting it.")] IMultiQuery SetDateTime2(string name, DateTime val); IMultiQuery SetTimeSpan(string name, TimeSpan val); IMultiQuery SetTimeAsTimeSpan(string name, TimeSpan val); @@ -346,6 +359,7 @@ public partial interface IMultiQuery /// The instance for method chain. IMultiQuery SetTime(string name, DateTime val); + // Obsolete since v5.0 /// /// Bind an instance of a to a named parameter /// using an NHibernate . @@ -353,6 +367,7 @@ public partial interface IMultiQuery /// The name of the parameter /// A non-null instance of a . /// The instance for method chain. + [Obsolete("Use SetDateTime instead.")] IMultiQuery SetTimestamp(string name, DateTime val); /// @@ -378,4 +393,4 @@ public partial interface IMultiQuery /// The instance for method chain. object GetResult(string key); } -} \ No newline at end of file +} diff --git a/src/NHibernate/IQuery.cs b/src/NHibernate/IQuery.cs index b15443bc9ae..2321d1e94de 100644 --- a/src/NHibernate/IQuery.cs +++ b/src/NHibernate/IQuery.cs @@ -3,7 +3,6 @@ using NHibernate.Transform; using NHibernate.Type; using System.Collections.Generic; -using System.Threading; namespace NHibernate { @@ -403,6 +402,8 @@ public partial interface IQuery /// /// The position of the parameter in the query string, numbered from 0 /// A non-null instance of a . + /// Since v5.0, does no more cut milliseconds. Use + /// for this IQuery SetDateTime(int position, DateTime val); /// @@ -411,9 +412,31 @@ public partial interface IQuery /// /// A non-null instance of a . /// The name of the parameter + /// Since v5.0, does no more cut milliseconds. Use + /// for this IQuery SetDateTime(string name, DateTime val); + /// + /// Bind an instance of a to an indexed parameter + /// using an NHibernate . + /// + /// The position of the parameter in the query string, numbered from 0 + /// A non-null instance of a . + IQuery SetDateTimeNoMs(int position, DateTime val); + + /// + /// Bind an instance of a to a named parameter + /// using an NHibernate . + /// + /// A non-null instance of a . + /// The name of the parameter + IQuery SetDateTimeNoMs(string name, DateTime val); + + // Since v5.0 + [Obsolete("Use SetDateTime instead, it uses DateTime2 with dialects supporting it.")] IQuery SetDateTime2(int position, DateTime val); + // Since v5.0 + [Obsolete("Use SetDateTime instead, it uses DateTime2 with dialects supporting it.")] IQuery SetDateTime2(string name, DateTime val); IQuery SetTimeSpan(int position, TimeSpan val); IQuery SetTimeSpan(string name, TimeSpan val); @@ -566,20 +589,24 @@ public partial interface IQuery /// A non-null instance of a . IQuery SetTime(string name, DateTime val); + // Obsolete since v5.0 /// /// Bind an instance of a to an indexed parameter /// using an NHibernate . /// /// The position of the parameter in the query string, numbered from 0 /// A non-null instance of a . + [Obsolete("Use SetDateTime instead.")] IQuery SetTimestamp(int position, DateTime val); + // Obsolete since v5.0 /// /// Bind an instance of a to a named parameter /// using an NHibernate . /// /// The name of the parameter /// A non-null instance of a . + [Obsolete("Use SetDateTime instead.")] IQuery SetTimestamp(string name, DateTime val); /// diff --git a/src/NHibernate/Impl/AbstractDetachedQuery.cs b/src/NHibernate/Impl/AbstractDetachedQuery.cs index 3418f4d5952..e32426aa550 100644 --- a/src/NHibernate/Impl/AbstractDetachedQuery.cs +++ b/src/NHibernate/Impl/AbstractDetachedQuery.cs @@ -237,6 +237,20 @@ public IDetachedQuery SetDateTime(string name, DateTime val) return this; } + /// + public IDetachedQuery SetDateTimeNoMs(int position, DateTime val) + { + SetParameter(position, val, NHibernateUtil.DateTimeNoMs); + return this; + } + + /// + public IDetachedQuery SetDateTimeNoMs(string name, DateTime val) + { + SetParameter(name, val, NHibernateUtil.DateTimeNoMs); + return this; + } + public IDetachedQuery SetDecimal(int position, decimal val) { SetParameter(position, val, NHibernateUtil.Decimal); @@ -357,12 +371,16 @@ public IDetachedQuery SetTime(string name, DateTime val) return this; } + // Since v5.0 + [Obsolete("Use SetDateTime instead.")] public IDetachedQuery SetTimestamp(int position, DateTime val) { SetParameter(position, val, NHibernateUtil.Timestamp); return this; } + // Since v5.0 + [Obsolete("Use SetDateTime instead.")] public IDetachedQuery SetTimestamp(string name, DateTime val) { SetParameter(name, val, NHibernateUtil.Timestamp); diff --git a/src/NHibernate/Impl/AbstractQueryImpl.cs b/src/NHibernate/Impl/AbstractQueryImpl.cs index ef9c5640694..e894c339d4d 100644 --- a/src/NHibernate/Impl/AbstractQueryImpl.cs +++ b/src/NHibernate/Impl/AbstractQueryImpl.cs @@ -4,13 +4,11 @@ using NHibernate.Engine; using NHibernate.Engine.Query; using NHibernate.Hql; -using NHibernate.Properties; using NHibernate.Proxy; using NHibernate.Transform; using NHibernate.Type; using NHibernate.Util; using System.Linq; -using System.Threading; namespace NHibernate.Impl { @@ -456,12 +454,28 @@ public IQuery SetDateTime(int position, DateTime val) return this; } + public IQuery SetDateTimeNoMs(int position, DateTime val) + { + SetParameter(position, val, NHibernateUtil.DateTimeNoMs); + return this; + } + + // Since v5.0 + [Obsolete("Use SetDateTime instead, it uses DateTime2 with dialects supporting it.")] + public IQuery SetDateTime2(int position, DateTime val) + { + SetParameter(position, val, NHibernateUtil.DateTime2); + return this; + } + public IQuery SetTime(int position, DateTime val) { SetParameter(position, val, NHibernateUtil.Time); return this; } + // Since v5.0 + [Obsolete("Use SetDateTime instead.")] public IQuery SetTimestamp(int position, DateTime val) { SetParameter(position, val, NHibernateUtil.Timestamp); @@ -558,12 +572,14 @@ public IQuery SetDateTime(string name, DateTime val) return this; } - public IQuery SetDateTime2(int position, DateTime val) + public IQuery SetDateTimeNoMs(string name, DateTime val) { - SetParameter(position, val, NHibernateUtil.DateTime2); + SetParameter(name, val, NHibernateUtil.DateTimeNoMs); return this; } + // Since v5.0 + [Obsolete("Use SetDateTime instead, it uses DateTime2 with dialects supporting it.")] public IQuery SetDateTime2(string name, DateTime val) { SetParameter(name, val, NHibernateUtil.DateTime2); @@ -606,6 +622,8 @@ public IQuery SetTime(string name, DateTime val) return this; } + // Since v5.0 + [Obsolete("Use SetDateTime instead.")] public IQuery SetTimestamp(string name, DateTime val) { SetParameter(name, val, NHibernateUtil.Timestamp); diff --git a/src/NHibernate/Impl/MultiQueryImpl.cs b/src/NHibernate/Impl/MultiQueryImpl.cs index 3e6c10ad830..1a6866434bf 100644 --- a/src/NHibernate/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Impl/MultiQueryImpl.cs @@ -158,6 +158,17 @@ public IMultiQuery SetDateTime(string name, DateTime val) return this; } + public IMultiQuery SetDateTimeNoMs(string name, DateTime val) + { + foreach (var query in queries) + { + query.SetDateTimeNoMs(name, val); + } + return this; + } + + // Since v5.0 + [Obsolete("Use SetDateTime instead, it uses DateTime2 with dialects supporting it.")] public IMultiQuery SetDateTime2(string name, DateTime val) { foreach (IQuery query in queries) @@ -293,6 +304,8 @@ public IMultiQuery SetTime(string name, DateTime val) return this; } + // Since v5.0 + [Obsolete("Use SetDateTime instead.")] public IMultiQuery SetTimestamp(string name, DateTime val) { foreach (IQuery query in queries) @@ -851,4 +864,4 @@ public string[] ReturnAliases get { return loader.ReturnAliases; } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 2cd835f76a8..1fa0fc79960 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -1206,7 +1206,7 @@ protected internal virtual DbCommand PrepareQueryCommand(QueryParameters queryPa IDriver driver = _factory.ConnectionProvider.Driver; driver.RemoveUnusedCommandParameters(command, sqlString); - driver.ExpandQueryParameters(command, sqlString); + driver.ExpandQueryParameters(command, sqlString, sqlCommand.ParameterTypes); } catch (HibernateException) { diff --git a/src/NHibernate/Mapping/Column.cs b/src/NHibernate/Mapping/Column.cs index dcf62b976fe..9ec4e8c18bf 100644 --- a/src/NHibernate/Mapping/Column.cs +++ b/src/NHibernate/Mapping/Column.cs @@ -229,8 +229,11 @@ private string GetDialectTypeName(Dialect.Dialect dialect, IMapping mapping) { if (IsCaracteristicsDefined()) { - // NH-1070 (the size should be 0 if the precision is defined) - return dialect.GetTypeName(GetSqlTypeCode(mapping), (!IsPrecisionDefined()) ? Length:0, Precision, Scale); + // NH-1070: the size should be 0 if the precision or scale is defined. + return dialect.GetTypeName( + GetSqlTypeCode(mapping), + IsPrecisionDefined() || IsScaleDefined() ? 0 : Length, + Precision, Scale); } else return dialect.GetTypeName(GetSqlTypeCode(mapping)); @@ -431,12 +434,17 @@ public string GetQuotedName() public bool IsCaracteristicsDefined() { - return IsLengthDefined() || IsPrecisionDefined(); + return IsLengthDefined() || IsPrecisionDefined() || IsScaleDefined(); } public bool IsPrecisionDefined() { - return _precision.HasValue || _scale.HasValue; + return _precision.HasValue; + } + + public bool IsScaleDefined() + { + return _scale.HasValue; } public bool IsLengthDefined() diff --git a/src/NHibernate/Mapping/ForeignKey.cs b/src/NHibernate/Mapping/ForeignKey.cs index dc9f1c94b92..67335b03c9a 100644 --- a/src/NHibernate/Mapping/ForeignKey.cs +++ b/src/NHibernate/Mapping/ForeignKey.cs @@ -123,12 +123,22 @@ private void AlignColumns(Table referencedTable) throw new FKUnmatchingColumnsException(sb.ToString()); } - using (var fkCols = ColumnIterator.GetEnumerator()) - using (var pkCols = referencedTable.PrimaryKey.ColumnIterator.GetEnumerator()) + AlignColumns(ColumnIterator, referencedTable.PrimaryKey.ColumnIterator); + } + + internal static void AlignColumns(IEnumerable fk, IEnumerable pk) + { + using (var fkCols = fk.GetEnumerator()) + using (var pkCols = pk.GetEnumerator()) { while (fkCols.MoveNext() && pkCols.MoveNext()) { - fkCols.Current.Length = pkCols.Current.Length; + if (pkCols.Current.IsLengthDefined() || fkCols.Current.IsLengthDefined()) + fkCols.Current.Length = pkCols.Current.Length; + if (pkCols.Current.IsPrecisionDefined() || fkCols.Current.IsPrecisionDefined()) + fkCols.Current.Precision = pkCols.Current.Precision; + if (pkCols.Current.IsScaleDefined() || fkCols.Current.IsScaleDefined()) + fkCols.Current.Scale = pkCols.Current.Scale; } } } diff --git a/src/NHibernate/Mapping/ManyToOne.cs b/src/NHibernate/Mapping/ManyToOne.cs index 645f3bafd59..1c29be9a55e 100644 --- a/src/NHibernate/Mapping/ManyToOne.cs +++ b/src/NHibernate/Mapping/ManyToOne.cs @@ -78,14 +78,8 @@ public void CreatePropertyRefConstraints(IDictionary pe IEnumerable ce = new SafetyEnumerable(property.ColumnIterator); - // NH : The four lines below was added to ensure that related columns have same length, - // like ForeignKey.AlignColumns() do - using (var fkCols = ConstraintColumns.GetEnumerator()) - using (var pkCols = ce.GetEnumerator()) - { - while (fkCols.MoveNext() && pkCols.MoveNext()) - fkCols.Current.Length = pkCols.Current.Length; - } + // NH : Ensure that related columns have same length + ForeignKey.AlignColumns(ConstraintColumns, ce); ForeignKey fk = Table.CreateForeignKey(ForeignKeyName, ConstraintColumns, ((EntityType)Type).GetAssociatedEntityName(), ce); @@ -94,4 +88,4 @@ public void CreatePropertyRefConstraints(IDictionary pe } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Mapping/SimpleValue.cs b/src/NHibernate/Mapping/SimpleValue.cs index 61d2134af9a..82765712d19 100644 --- a/src/NHibernate/Mapping/SimpleValue.cs +++ b/src/NHibernate/Mapping/SimpleValue.cs @@ -261,6 +261,10 @@ private IType GetHeuristicType() { result = TypeFactory.BuiltInType(typeName, Convert.ToByte(col.Precision), Convert.ToByte(col.Scale)); } + else if (col.IsScaleDefined()) + { + result = TypeFactory.BuiltInType(typeName, col.Scale); + } } return result ?? TypeFactory.HeuristicType(typeName, typeParameters); } diff --git a/src/NHibernate/NHibernate.csproj b/src/NHibernate/NHibernate.csproj index b3e2f175919..dd3fa73adaa 100644 --- a/src/NHibernate/NHibernate.csproj +++ b/src/NHibernate/NHibernate.csproj @@ -1,38 +1,30 @@  - An object persistence library for relational databases. - net461 $(NoWarn);3001;3002;3003;3005;1591 True ..\NHibernate.snk true - True - - - - - All @@ -42,11 +34,9 @@ - - - + \ No newline at end of file diff --git a/src/NHibernate/NHibernateUtil.cs b/src/NHibernate/NHibernateUtil.cs index 9bacc4a8004..aa5d9b02ca2 100644 --- a/src/NHibernate/NHibernateUtil.cs +++ b/src/NHibernate/NHibernateUtil.cs @@ -16,7 +16,8 @@ namespace NHibernate /// /// Provides access to the full range of NHibernate built-in types. /// IType instances may be used to bind values to query parameters. - /// Also a factory for new Blobs and Clobs. + /// if needing to specify type size, + /// precision or scale. /// public static partial class NHibernateUtil { @@ -33,10 +34,11 @@ static NHibernateUtil() clrTypeToNHibernateType[type.ReturnedClass] = type; } - // There are multiple possibilites for boolean and strings. + // There are multiple possibilites for boolean, strings and datetime. // Override so that we use the most natural mapping. clrTypeToNHibernateType[Boolean.ReturnedClass] = Boolean; clrTypeToNHibernateType[String.ReturnedClass] = String; + clrTypeToNHibernateType[DateTime.ReturnedClass] = DateTime; } /// @@ -111,27 +113,45 @@ public static IType GuessType(System.Type type) public static readonly CultureInfoType CultureInfo = new CultureInfoType(); /// - /// NHibernate date type + /// NHibernate date time type. Since v5.0, does no more cut milliseconds. /// + /// Use if needing cutting milliseconds. public static readonly DateTimeType DateTime = new DateTimeType(); /// - /// NHibernate date type + /// NHibernate date time cutting milliseconds type + /// + public static readonly DateTimeNoMsType DateTimeNoMs = new DateTimeNoMsType(); + + // Obsolete since v5.0 + /// + /// NHibernate date time 2 type /// + [Obsolete("Use DateTimeType instead, it uses DateTime2 with dialects supporting it.")] public static readonly DateTime2Type DateTime2 = new DateTime2Type(); /// - /// NHibernate local date type + /// NHibernate local date time type /// public static readonly LocalDateTimeType LocalDateTime = new LocalDateTimeType(); /// - /// NHibernate utc date type + /// NHibernate utc date time type /// public static readonly UtcDateTimeType UtcDateTime = new UtcDateTimeType(); /// - /// NHibernate date type + /// NHibernate local date time cutting milliseconds type + /// + public static readonly LocalDateTimeNoMsType LocalDateTimeNoMs = new LocalDateTimeNoMsType(); + + /// + /// NHibernate utc date time cutting milliseconds type + /// + public static readonly UtcDateTimeNoMsType UtcDateTimeNoMs = new UtcDateTimeNoMsType(); + + /// + /// NHibernate date time with offset type /// public static readonly DateTimeOffsetType DateTimeOffset = new DateTimeOffsetType(); @@ -230,17 +250,17 @@ public static IType GuessType(System.Type type) /// public static readonly TimeSpanType TimeSpan = new TimeSpanType(); + // Obsolete since v5.0 /// /// NHibernate Timestamp type /// + [Obsolete("Use DateTime instead.")] public static readonly TimestampType Timestamp = new TimestampType(); - public static readonly DbTimestampType DbTimestamp = new DbTimestampType(); - /// - /// NHibernate timestamp utc type. + /// NHibernate Timestamp type, seeded db side. /// - public static readonly TimestampUtcType TimestampUtc = new TimestampUtcType(); + public static readonly DbTimestampType DbTimestamp = new DbTimestampType(); /// /// NHibernate TrueFalse type diff --git a/src/NHibernate/SqlTypes/DateTime2SqlType.cs b/src/NHibernate/SqlTypes/DateTime2SqlType.cs new file mode 100644 index 00000000000..27d07c8d475 --- /dev/null +++ b/src/NHibernate/SqlTypes/DateTime2SqlType.cs @@ -0,0 +1,27 @@ +using System; +using System.Data; + +namespace NHibernate.SqlTypes +{ + /// + /// Describes the details of a . + /// + [Serializable] + public class DateTime2SqlType : SqlType + { + /// + /// Initializes a new instance of the class. + /// + public DateTime2SqlType() : base(DbType.DateTime2) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of digit below seconds. + public DateTime2SqlType(byte fractionalSecondsPrecision) : base(DbType.DateTime2, fractionalSecondsPrecision) + { + } + } +} diff --git a/src/NHibernate/SqlTypes/DateTimeOffsetSqlType.cs b/src/NHibernate/SqlTypes/DateTimeOffsetSqlType.cs new file mode 100644 index 00000000000..ffcd5323505 --- /dev/null +++ b/src/NHibernate/SqlTypes/DateTimeOffsetSqlType.cs @@ -0,0 +1,27 @@ +using System; +using System.Data; + +namespace NHibernate.SqlTypes +{ + /// + /// Describes the details of a . + /// + [Serializable] + public class DateTimeOffsetSqlType : SqlType + { + /// + /// Initializes a new instance of the class. + /// + public DateTimeOffsetSqlType() : base(DbType.DateTimeOffset) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of digit below seconds. + public DateTimeOffsetSqlType(byte fractionalSecondsPrecision) : base(DbType.DateTimeOffset, fractionalSecondsPrecision) + { + } + } +} diff --git a/src/NHibernate/SqlTypes/DateTimeSqlType.cs b/src/NHibernate/SqlTypes/DateTimeSqlType.cs new file mode 100644 index 00000000000..697a272a876 --- /dev/null +++ b/src/NHibernate/SqlTypes/DateTimeSqlType.cs @@ -0,0 +1,27 @@ +using System; +using System.Data; + +namespace NHibernate.SqlTypes +{ + /// + /// Describes the details of a . + /// + [Serializable] + public class DateTimeSqlType : SqlType + { + /// + /// Initializes a new instance of the class. + /// + public DateTimeSqlType() : base(DbType.DateTime) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of digit below seconds. + public DateTimeSqlType(byte fractionalSecondsPrecision) : base(DbType.DateTime, fractionalSecondsPrecision) + { + } + } +} diff --git a/src/NHibernate/SqlTypes/SqlType.cs b/src/NHibernate/SqlTypes/SqlType.cs index 23eaee6fed5..c32c377723c 100644 --- a/src/NHibernate/SqlTypes/SqlType.cs +++ b/src/NHibernate/SqlTypes/SqlType.cs @@ -50,6 +50,13 @@ public SqlType(DbType dbType, byte precision, byte scale) precisionDefined = true; } + public SqlType(DbType dbType, byte scale) + { + this.dbType = dbType; + this.scale = scale; + ScaleDefined = true; + } + public DbType DbType { get { return dbType; } @@ -80,6 +87,8 @@ public bool PrecisionDefined get { return precisionDefined; } } + public bool ScaleDefined { get; } + #region System.Object Members public override int GetHashCode() @@ -96,6 +105,10 @@ public override int GetHashCode() { hashCode = (DbType.GetHashCode() / 3) + (Precision.GetHashCode() / 3) + (Scale.GetHashCode() / 3); } + else if (ScaleDefined) + { + hashCode = DbType.GetHashCode() / 3 + Scale.GetHashCode() / 3; + } else { hashCode = DbType.GetHashCode(); @@ -125,12 +138,16 @@ public bool Equals(SqlType rhsSqlType) { return (DbType.Equals(rhsSqlType.DbType)) && (Precision == rhsSqlType.Precision) && (Scale == rhsSqlType.Scale); } + if (ScaleDefined) + { + return DbType.Equals(rhsSqlType.DbType) && Scale == rhsSqlType.Scale; + } return (DbType.Equals(rhsSqlType.DbType)); } public override string ToString() { - if (!LengthDefined && !PrecisionDefined) + if (!LengthDefined && !PrecisionDefined && !ScaleDefined) { // Shortcut return DbType.ToString(); @@ -147,10 +164,14 @@ public override string ToString() { result.Append("(Precision=").Append(Precision).Append(", ").Append("Scale=").Append(Scale).Append(')'); } + else if (ScaleDefined) + { + result.Append("Scale=").Append(Scale).Append(')'); + } return result.ToString(); } #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/SqlTypes/SqlTypeFactory.cs b/src/NHibernate/SqlTypes/SqlTypeFactory.cs index 290d4472f5a..20e9ed75270 100644 --- a/src/NHibernate/SqlTypes/SqlTypeFactory.cs +++ b/src/NHibernate/SqlTypes/SqlTypeFactory.cs @@ -14,16 +14,16 @@ public static class SqlTypeFactory // key = typeof(sqlType).Name : ie - BinarySqlType(l), BooleanSqlType, DecimalSqlType(p,s) // value = SqlType private static readonly ConcurrentDictionary SqlTypes = - new ConcurrentDictionary(4 * System.Environment.ProcessorCount, 128); + new ConcurrentDictionary(4 * Environment.ProcessorCount, 128); public static readonly SqlType Guid = new SqlType(DbType.Guid); public static readonly SqlType Boolean = new SqlType(DbType.Boolean); public static readonly SqlType Byte = new SqlType(DbType.Byte); public static readonly SqlType Currency = new SqlType(DbType.Currency); public static readonly SqlType Date = new SqlType(DbType.Date); - public static readonly SqlType DateTime = new SqlType(DbType.DateTime); - public static readonly SqlType DateTime2 = new SqlType(DbType.DateTime2); - public static readonly SqlType DateTimeOffSet = new SqlType(DbType.DateTimeOffset); + public static readonly SqlType DateTime = new DateTimeSqlType(); + public static readonly SqlType DateTime2 = new DateTime2SqlType(); + public static readonly SqlType DateTimeOffSet = new DateTimeOffsetSqlType(); public static readonly SqlType Decimal = new SqlType(DbType.Decimal); public static readonly SqlType Double = new SqlType(DbType.Double); public static readonly SqlType Int16 = new SqlType(DbType.Int16); @@ -31,19 +31,19 @@ public static class SqlTypeFactory public static readonly SqlType Int64 = new SqlType(DbType.Int64); public static readonly SqlType SByte = new SqlType(DbType.SByte); public static readonly SqlType Single = new SqlType(DbType.Single); - public static readonly SqlType Time = new SqlType(DbType.Time); + public static readonly SqlType Time = new TimeSqlType(); public static readonly SqlType UInt16 = new SqlType(DbType.UInt16); public static readonly SqlType UInt32 = new SqlType(DbType.UInt32); public static readonly SqlType UInt64 = new SqlType(DbType.UInt64); public static readonly SqlType[] NoTypes = new SqlType[0]; - private delegate SqlType TypeWithLenCreateDelegate(int length); // Func + private delegate T TypeWithLenOrScaleCreateDelegate(TDim lengthOrScale); // Func - private static T GetTypeWithLen(int length, TypeWithLenCreateDelegate createDelegate) where T : SqlType + private static T GetTypeWithLenOrScale(TDim lengthOrScale, TypeWithLenOrScaleCreateDelegate createDelegate) where T : SqlType { - string key = GetKeyForLengthBased(typeof (T).Name, length); - SqlType result = SqlTypes.GetOrAdd(key, k => createDelegate(length)); + var key = GetKeyForLengthOrScaleBased(typeof(T).Name, lengthOrScale); + var result = SqlTypes.GetOrAdd(key, k => createDelegate(lengthOrScale)); return (T) result; } @@ -56,27 +56,47 @@ private static SqlType GetTypeWithPrecision(DbType dbType, byte precision, byte public static AnsiStringSqlType GetAnsiString(int length) { - return GetTypeWithLen(length, l => new AnsiStringSqlType(l)); + return GetTypeWithLenOrScale(length, l => new AnsiStringSqlType(l)); } public static BinarySqlType GetBinary(int length) { - return GetTypeWithLen(length, l => new BinarySqlType(l)); + return GetTypeWithLenOrScale(length, l => new BinarySqlType(l)); } public static BinaryBlobSqlType GetBinaryBlob(int length) { - return GetTypeWithLen(length, l => new BinaryBlobSqlType(l)); + return GetTypeWithLenOrScale(length, l => new BinaryBlobSqlType(l)); } public static StringSqlType GetString(int length) { - return GetTypeWithLen(length, l => new StringSqlType(l)); + return GetTypeWithLenOrScale(length, l => new StringSqlType(l)); } public static StringClobSqlType GetStringClob(int length) { - return GetTypeWithLen(length, l => new StringClobSqlType(l)); + return GetTypeWithLenOrScale(length, l => new StringClobSqlType(l)); + } + + public static DateTimeSqlType GetDateTime(byte fractionalSecondsPrecision) + { + return GetTypeWithLenOrScale(fractionalSecondsPrecision, l => new DateTimeSqlType(l)); + } + + public static DateTime2SqlType GetDateTime2(byte fractionalSecondsPrecision) + { + return GetTypeWithLenOrScale(fractionalSecondsPrecision, l => new DateTime2SqlType(l)); + } + + public static DateTimeOffsetSqlType GetDateTimeOffset(byte fractionalSecondsPrecision) + { + return GetTypeWithLenOrScale(fractionalSecondsPrecision, l => new DateTimeOffsetSqlType(l)); + } + + public static TimeSqlType GetTime(byte fractionalSecondsPrecision) + { + return GetTypeWithLenOrScale(fractionalSecondsPrecision, l => new TimeSqlType(l)); } [MethodImpl(MethodImplOptions.Synchronized)] @@ -85,9 +105,9 @@ public static SqlType GetSqlType(DbType dbType, byte precision, byte scale) return GetTypeWithPrecision(dbType, precision, scale); } - private static string GetKeyForLengthBased(string name, int length) + private static string GetKeyForLengthOrScaleBased(string name, T lengthOrScale) { - return name + "(" + length + ")"; + return name + "(" + lengthOrScale + ")"; } private static string GetKeyForPrecisionScaleBased(string name, byte precision, byte scale) diff --git a/src/NHibernate/SqlTypes/TimeSqlType.cs b/src/NHibernate/SqlTypes/TimeSqlType.cs new file mode 100644 index 00000000000..069adf4adda --- /dev/null +++ b/src/NHibernate/SqlTypes/TimeSqlType.cs @@ -0,0 +1,27 @@ +using System; +using System.Data; + +namespace NHibernate.SqlTypes +{ + /// + /// Describes the details of a . + /// + [Serializable] + public class TimeSqlType : SqlType + { + /// + /// Initializes a new instance of the class. + /// + public TimeSqlType() : base(DbType.Time) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of digit below seconds. + public TimeSqlType(byte fractionalSecondsPrecision) : base(DbType.Time, fractionalSecondsPrecision) + { + } + } +} diff --git a/src/NHibernate/Type/AbstractDateTimeSpecificKindType.cs b/src/NHibernate/Type/AbstractDateTimeSpecificKindType.cs deleted file mode 100644 index ec12074cb68..00000000000 --- a/src/NHibernate/Type/AbstractDateTimeSpecificKindType.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Data.Common; -using NHibernate.Engine; - -namespace NHibernate.Type -{ - [Serializable] - public abstract class AbstractDateTimeSpecificKindType : DateTimeType - { - protected abstract DateTimeKind DateTimeKind { get; } - - protected virtual DateTime CreateDateTime(DateTime dateValue) - { - return new DateTime(dateValue.Year, dateValue.Month, dateValue.Day, dateValue.Hour, dateValue.Minute, dateValue.Second, DateTimeKind); - } - - public override object FromStringValue(string xml) - { - return DateTime.SpecifyKind(DateTime.Parse(xml), DateTimeKind); - } - - public override int GetHashCode(object x) - { - int hashCode = base.GetHashCode(x); - unchecked - { - hashCode = 31*hashCode + ((DateTime) x).Kind.GetHashCode(); - } - return hashCode; - } - - public override bool IsEqual(object x, object y) - { - if (x == y) - { - return true; - } - - if (x == null || y == null) - { - return false; - } - - return base.IsEqual(x, y) && ((DateTime) x).Kind == ((DateTime) y).Kind; - } - - public override void Set(DbCommand st, object value, int index, ISessionImplementor session) - { - var dateValue = (DateTime) value; - st.Parameters[index].Value = CreateDateTime(dateValue); - } - - public override object Get(DbDataReader rs, int index, ISessionImplementor session) - { - try - { - DateTime dbValue = Convert.ToDateTime(rs[index]); - return CreateDateTime(dbValue); - } - catch (Exception ex) - { - throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex); - } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Type/AbstractDateTimeType.cs b/src/NHibernate/Type/AbstractDateTimeType.cs new file mode 100644 index 00000000000..78d1423f8e7 --- /dev/null +++ b/src/NHibernate/Type/AbstractDateTimeType.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data.Common; +using System.Globalization; +using NHibernate.Engine; +using NHibernate.SqlTypes; + +namespace NHibernate.Type +{ + /// + /// Base class for date time types. + /// + [Serializable] + public abstract partial class AbstractDateTimeType : PrimitiveType, IIdentifierType, ILiteralType, IVersionType + { + private static readonly DateTime BaseDateValue = DateTime.MinValue; + + /// + /// Returns the for the type. + /// + protected virtual DateTimeKind Kind => DateTimeKind.Unspecified; + + /// + public override System.Type ReturnedClass => typeof(DateTime); + + /// + /// Retrieve the current system time. + /// + /// if is , + /// otherwise. + protected virtual DateTime Now => Kind == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.Now; + + /// + /// Default constructor. + /// + protected AbstractDateTimeType() : base(SqlTypeFactory.DateTime) + { + } + + /// + /// Constructor for overriding the default . + /// + /// The to use. + protected AbstractDateTimeType(SqlType sqlTypeDateTime) : base(sqlTypeDateTime) + { + } + + /// + /// Adjust the date time value for this type from an arbitrary date time value. + /// + /// The to adjust. + /// A . + protected virtual DateTime AdjustDateTime(DateTime dateValue) => + Kind == DateTimeKind.Unspecified ? dateValue : DateTime.SpecifyKind(dateValue, Kind); + + /// + public override object Get(DbDataReader rs, int index, ISessionImplementor session) => + GetDateTime(rs, index, session); + + /// + public override object Get(DbDataReader rs, string name, ISessionImplementor session) => + Get(rs, rs.GetOrdinal(name), session); + + /// + /// Get the in the for the Property. + /// + /// The that contains the value. + /// The index of the field to get the value from. + /// The session for which the operation is done. + /// An object with the value from the database. + protected virtual DateTime GetDateTime(DbDataReader rs, int index, ISessionImplementor session) + { + try + { + return AdjustDateTime(Convert.ToDateTime(rs[index])); + } + catch (Exception ex) + { + throw new FormatException($"Input string '{rs[index]}' was not in the correct format.", ex); + } + } + + /// + public override void Set(DbCommand st, object value, int index, ISessionImplementor session) + { + var dateValue = (DateTime) value; + // We could try convert. This is always doable when going to local. But the other way may encounter + // ambiguous times. .Net then always assumes the time to be without daylight shift, causing the ambiguous + // hour with daylight shift to be always wrongly converted. So better just fail. + if (Kind != DateTimeKind.Unspecified && dateValue.Kind != Kind) + throw new ArgumentException($"{Name} expect date kind {Kind} but it is {dateValue.Kind}", nameof(value)); + st.Parameters[index].Value = AdjustDateTime(dateValue); + } + + #region IVersionType Members + + /// + public object Next(object current, ISessionImplementor session) => + Seed(session); + + /// + /// Round a according to specified resolution. + /// + /// The value to round. + /// The resolution in ticks (100ns). + /// A rounded . + public static DateTime Round(DateTime value, long resolution) => + value.AddTicks(-(value.Ticks % resolution)); + + /// + public virtual object Seed(ISessionImplementor session) => + session == null ? Now : Round(Now, session.Factory.Dialect.TimestampResolutionInTicks); + + /// + public virtual IComparer Comparator => Comparer.Default; + + #endregion + + /// + /// Compares two object and also compare its Kind if needed, which is not used by the + /// .Net Framework implementation. + /// + /// The first date time to compare. + /// The second date time to compare. + /// if they are equals, otherwise. + public override bool IsEqual(object x, object y) => + base.IsEqual(x, y) && + (Kind == DateTimeKind.Unspecified || x == null || ((DateTime) x).Kind == ((DateTime) y).Kind); + + /// + public override string ToString(object val) => + ((DateTime) val).ToString(CultureInfo.CurrentCulture); + + /// + public object StringToObject(string xml) => + string.IsNullOrEmpty(xml) ? null : FromStringValue(xml); + + /// + public override object FromStringValue(string xml) + { + // Parsing with .Net always yield a Local date. + var date = DateTime.Parse(xml); + if (Kind == DateTimeKind.Utc) + date = date.ToUniversalTime(); + return date; + } + + /// + public override System.Type PrimitiveClass => typeof(DateTime); + + /// + public override object DefaultValue => BaseDateValue; + + /// + public override string ObjectToSQLString(object value, Dialect.Dialect dialect) => + "'" + (DateTime) value + "'"; + } +} diff --git a/src/NHibernate/Type/AbstractType.cs b/src/NHibernate/Type/AbstractType.cs index eacc9844ce1..4b7f95fabec 100644 --- a/src/NHibernate/Type/AbstractType.cs +++ b/src/NHibernate/Type/AbstractType.cs @@ -275,4 +275,4 @@ public abstract object Replace(object original, object current, ISessionImplemen public abstract bool IsDirty(object old, object current, bool[] checkable, ISessionImplementor session); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/ClassMetaType.cs b/src/NHibernate/Type/ClassMetaType.cs index 186fc0117ac..248bbccd0ff 100644 --- a/src/NHibernate/Type/ClassMetaType.cs +++ b/src/NHibernate/Type/ClassMetaType.cs @@ -16,7 +16,7 @@ public partial class ClassMetaType : AbstractType { public override SqlType[] SqlTypes(IMapping mapping) { - return new SqlType[] { NHibernateUtil.String.SqlType }; + return NHibernateUtil.String.SqlTypes(mapping); } public override int GetColumnSpan(IMapping mapping) diff --git a/src/NHibernate/Type/CollectionType.cs b/src/NHibernate/Type/CollectionType.cs index 8c7408c8790..ce7aa8998fa 100644 --- a/src/NHibernate/Type/CollectionType.cs +++ b/src/NHibernate/Type/CollectionType.cs @@ -98,7 +98,7 @@ public override void NullSafeSet(DbCommand cmd, object value, int index, ISessio { } - public override SqlType[] SqlTypes(IMapping session) + public override SqlType[] SqlTypes(IMapping mapping) { return NoSqlTypes; } diff --git a/src/NHibernate/Type/ComponentType.cs b/src/NHibernate/Type/ComponentType.cs index 9bf4cdc4487..05b8a24381e 100644 --- a/src/NHibernate/Type/ComponentType.cs +++ b/src/NHibernate/Type/ComponentType.cs @@ -678,4 +678,4 @@ public int GetPropertyIndex(string name) throw new PropertyNotFoundException(ReturnedClass, name); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/CompositeCustomType.cs b/src/NHibernate/Type/CompositeCustomType.cs index 1285e39e84d..637c027b884 100644 --- a/src/NHibernate/Type/CompositeCustomType.cs +++ b/src/NHibernate/Type/CompositeCustomType.cs @@ -262,4 +262,4 @@ public override bool[] ToColumnNullness(object value, IMapping mapping) } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/CustomType.cs b/src/NHibernate/Type/CustomType.cs index 8f7444417fd..c44a3b7ccad 100644 --- a/src/NHibernate/Type/CustomType.cs +++ b/src/NHibernate/Type/CustomType.cs @@ -64,11 +64,7 @@ public CustomType(System.Type userTypeClass, IDictionary paramet sqlTypes = userType.SqlTypes; } - /// - /// - /// - /// - /// + /// public override SqlType[] SqlTypes(IMapping mapping) { return sqlTypes; diff --git a/src/NHibernate/Type/DateTime2Type.cs b/src/NHibernate/Type/DateTime2Type.cs index da92f42041b..813f6de193b 100644 --- a/src/NHibernate/Type/DateTime2Type.cs +++ b/src/NHibernate/Type/DateTime2Type.cs @@ -1,67 +1,33 @@ using System; using System.Data; -using System.Data.Common; -using NHibernate.Engine; using NHibernate.SqlTypes; namespace NHibernate.Type { + // Obsolete since v5.0 /// - /// Maps a Property to a + /// Maps a Property to a /// + [Obsolete("Use DateTimeType instead, it uses DateTime2 with dialects supporting it.")] [Serializable] - public partial class DateTime2Type : DateTimeType + public class DateTime2Type : AbstractDateTimeType { - /// - internal DateTime2Type() : base(SqlTypeFactory.DateTime2) + /// + /// Default constructor. + /// + public DateTime2Type() : base(SqlTypeFactory.DateTime2) { } - public override string Name + /// + /// Constructor for specifying a datetime with a scale. Use . + /// + /// The sql type to use for the type. + public DateTime2Type(DateTime2SqlType sqlType) : base(sqlType) { - get { return "DateTime2"; } } - public override object Get(DbDataReader rs, int index, ISessionImplementor session) - { - try - { - return Convert.ToDateTime(rs[index]); - } - catch (Exception ex) - { - throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex); - } - } - - public override void Set(DbCommand st, object value, int index, ISessionImplementor session) - { - st.Parameters[index].Value = (DateTime) value; - } - - public override bool IsEqual(object x, object y) - { - if (x == y) - { - return true; - } - - if (x == null || y == null) - { - return false; - } - - return x.Equals(y); - } - - public override object Next(object current, ISessionImplementor session) - { - return Seed(session); - } - - public override object Seed(ISessionImplementor session) - { - return DateTime.Now; - } + /// + public override string Name => "DateTime2"; } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/DateTimeNoMsType.cs b/src/NHibernate/Type/DateTimeNoMsType.cs new file mode 100644 index 00000000000..0dcf8ac1bb1 --- /dev/null +++ b/src/NHibernate/Type/DateTimeNoMsType.cs @@ -0,0 +1,86 @@ +using System; +using NHibernate.Engine; +using System.Data; + +namespace NHibernate.Type +{ + /// + /// Maps a property to a column that + /// stores date & time down to the accuracy of a second. + /// + /// + /// This only stores down to a second, so if you are looking for the most accurate + /// date and time storage your provider can give you use the + /// or the . This type is equivalent to the Hibernate DateTime type. + /// + [Serializable] + public partial class DateTimeNoMsType : AbstractDateTimeType + { + /// + public override string Name => "DateTimeNoMs"; + + /// + protected override DateTime AdjustDateTime(DateTime dateValue) => + base.AdjustDateTime(new DateTime(dateValue.Year, dateValue.Month, dateValue.Day, dateValue.Hour, dateValue.Minute, dateValue.Second)); + + #region IVersionType Members + + public override object Seed(ISessionImplementor session) + { + return Round(Now, TimeSpan.TicksPerSecond); + } + + #endregion + + /// + public override bool IsEqual(object x, object y) + { + if (x == y) + { + return true; + } + + if (x == null || y == null) + { + return false; + } + + var date1 = (DateTime) x; + var date2 = (DateTime) y; + + if (date1.Equals(date2)) + return true; + + return (date1.Equals(date2) || + date1.Year == date2.Year && + date1.Month == date2.Month && + date1.Day == date2.Day && + date1.Hour == date2.Hour && + date1.Minute == date2.Minute && + date1.Second == date2.Second) && + (Kind == DateTimeKind.Unspecified || date1.Kind == date2.Kind); + } + + /// + public override int GetHashCode(object x) + { + // Custom hash code implementation because DateTimeType is only accurate + // up to seconds. + var date = (DateTime) x; + var hashCode = 1; + unchecked + { + hashCode = 31 * hashCode + date.Second; + hashCode = 31 * hashCode + date.Minute; + hashCode = 31 * hashCode + date.Hour; + hashCode = 31 * hashCode + date.Day; + hashCode = 31 * hashCode + date.Month; + hashCode = 31 * hashCode + date.Year; + if (Kind != DateTimeKind.Unspecified) + hashCode = 31 * hashCode + date.Kind.GetHashCode(); + } + + return hashCode; + } + } +} diff --git a/src/NHibernate/Type/DateTimeOffSetType.cs b/src/NHibernate/Type/DateTimeOffSetType.cs index 2afe4947d69..8af80b06bc8 100644 --- a/src/NHibernate/Type/DateTimeOffSetType.cs +++ b/src/NHibernate/Type/DateTimeOffSetType.cs @@ -16,9 +16,18 @@ public partial class DateTimeOffsetType : PrimitiveType, IIdentifierType, ILiter { static readonly DateTimeOffset BaseDateValue = DateTimeOffset.MinValue; - /// - public DateTimeOffsetType() - : base(SqlTypeFactory.DateTimeOffSet) + /// + /// Default constructor. + /// + public DateTimeOffsetType() : base(SqlTypeFactory.DateTimeOffSet) + { + } + + /// + /// Constructor for specifying a datetimeoffset with a scale. Use . + /// + /// The sql type to use for the type. + public DateTimeOffsetType(DateTimeOffsetSqlType sqlType) : base(sqlType) { } @@ -77,10 +86,18 @@ public object Next(object current, ISessionImplementor session) return Seed(session); } - public object Seed(ISessionImplementor session) - { - return DateTimeOffset.Now; - } + /// + /// Truncate a according to specified resolution. + /// + /// The value to round. + /// The resolution in ticks (100ns). + /// A rounded . + public static DateTimeOffset Round(DateTimeOffset value, long resolution) => + value.AddTicks(-(value.Ticks % resolution)); + + /// + public virtual object Seed(ISessionImplementor session) => + session == null ? DateTimeOffset.Now : Round(DateTimeOffset.Now, session.Factory.Dialect.TimestampResolutionInTicks); public override bool IsEqual(object x, object y) { diff --git a/src/NHibernate/Type/DateTimeType.cs b/src/NHibernate/Type/DateTimeType.cs index 4f0de061c06..04d00db2db2 100644 --- a/src/NHibernate/Type/DateTimeType.cs +++ b/src/NHibernate/Type/DateTimeType.cs @@ -1,163 +1,37 @@ using System; -using System.Collections; -using System.Data.Common; -using NHibernate.Engine; -using NHibernate.SqlTypes; -using System.Collections.Generic; using System.Data; +using NHibernate.SqlTypes; namespace NHibernate.Type { /// - /// Maps a Property to a column that - /// stores date & time down to the accuracy of a second. + /// Maps a property to a column that + /// stores date & time down to the accuracy of the database. /// /// - /// This only stores down to a second, so if you are looking for the most accurate - /// date and time storage your provider can give you use the . - /// or the + /// If you are looking for the most accurate date and time storage accross databases use the + /// . If you are looking for the Hibernate DateTime equivalent, + /// use the . /// [Serializable] - public partial class DateTimeType : PrimitiveType, IIdentifierType, ILiteralType, IVersionType + public class DateTimeType : AbstractDateTimeType { - private static readonly DateTime BaseDateValue = DateTime.MinValue; - - /// - public DateTimeType() : base(SqlTypeFactory.DateTime) - { - } - - public DateTimeType(SqlType sqlTypeDateTime) : base(sqlTypeDateTime) - { - } - - /// - public override string Name - { - get { return "DateTime"; } - } - - public override object Get(DbDataReader rs, int index, ISessionImplementor session) - { - try - { - DateTime dbValue = Convert.ToDateTime(rs[index]); - return new DateTime(dbValue.Year, dbValue.Month, dbValue.Day, dbValue.Hour, dbValue.Minute, dbValue.Second); - } - catch (Exception ex) - { - throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex); - } - } - - public override object Get(DbDataReader rs, string name, ISessionImplementor session) - { - return Get(rs, rs.GetOrdinal(name), session); - } - - public override System.Type ReturnedClass - { - get { return typeof(DateTime); } - } - - public override void Set(DbCommand st, object value, int index, ISessionImplementor session) - { - DateTime dateValue = (DateTime) value; - st.Parameters[index].Value = - new DateTime(dateValue.Year, dateValue.Month, dateValue.Day, dateValue.Hour, dateValue.Minute, dateValue.Second); - } - - #region IVersionType Members - - public virtual object Next(object current, ISessionImplementor session) - { - return Seed(session); - } - - public virtual object Seed(ISessionImplementor session) + /// + /// Default constructor. + /// + public DateTimeType() { - return TimestampType.Round(DateTime.Now, TimeSpan.TicksPerSecond); } - public override bool IsEqual(object x, object y) + /// + /// Constructor for specifying a datetime with a scale. Use . + /// + /// The sql type to use for the type. + public DateTimeType(DateTimeSqlType sqlType) : base(sqlType) { - if (x == y) - { - return true; - } - - if (x == null || y == null) - { - return false; - } - - DateTime date1 = (DateTime) x; - DateTime date2 = (DateTime) y; - - if(date1.Equals(date2)) - return true; - - return (date1.Year == date2.Year && - date1.Month == date2.Month && - date1.Day == date2.Day && - date1.Hour == date2.Hour && - date1.Minute == date2.Minute && - date1.Second == date2.Second); - } - - public virtual IComparer Comparator - { - get { return Comparer.Default; } - } - - #endregion - - public override int GetHashCode(object x) - { - // Custom hash code implementation because DateTimeType is only accurate - // up to seconds. - DateTime date = (DateTime) x; - int hashCode = 1; - unchecked - { - hashCode = 31 * hashCode + date.Second; - hashCode = 31 * hashCode + date.Minute; - hashCode = 31 * hashCode + date.Hour; - hashCode = 31 * hashCode + date.Day; - hashCode = 31 * hashCode + date.Month; - hashCode = 31 * hashCode + date.Year; - } - return hashCode; - } - - public override string ToString(object val) - { - return ((DateTime) val).ToString(); } - public object StringToObject(string xml) - { - return string.IsNullOrEmpty(xml) ? null : FromStringValue(xml); - } - - public override object FromStringValue(string xml) - { - return DateTime.Parse(xml); - } - - public override System.Type PrimitiveClass - { - get { return typeof(DateTime); } - } - - public override object DefaultValue - { - get { return BaseDateValue; } - } - - public override string ObjectToSQLString(object value, Dialect.Dialect dialect) - { - return "'" + ((DateTime)value) + "'"; - } + /// + public override string Name => "DateTime"; } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/DateType.cs b/src/NHibernate/Type/DateType.cs index f0178857978..b6626a49b20 100644 --- a/src/NHibernate/Type/DateType.cs +++ b/src/NHibernate/Type/DateType.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.Common; -using NHibernate.Engine; using NHibernate.SqlTypes; using NHibernate.UserTypes; @@ -13,7 +11,7 @@ namespace NHibernate.Type /// column /// [Serializable] - public class DateType : PrimitiveType, IIdentifierType, ILiteralType, IParameterizedType + public class DateType : AbstractDateTimeType, IParameterizedType { private static readonly IInternalLogger _log = LoggerProvider.LoggerFor(typeof(DateType)); // Since v5.0 @@ -21,52 +19,24 @@ public class DateType : PrimitiveType, IIdentifierType, ILiteralType, IParameter public const string BaseValueParameterName = "BaseValue"; // Since v5.0 [Obsolete("Use DateTime.MinValue.")] - public static readonly DateTime BaseDateValue = _baseDateValue; + public static readonly DateTime BaseDateValue = DateTime.MinValue; private DateTime customBaseDate = _baseDateValue; private static readonly DateTime _baseDateValue = DateTime.MinValue; - /// + /// Default constructor public DateType() : base(SqlTypeFactory.Date) { } - /// - public override string Name - { - get { return "Date"; } - } - - public override object Get(DbDataReader rs, int index, ISessionImplementor session) - { - try - { - DateTime dbValue = Convert.ToDateTime(rs[index]); - return dbValue.Date; - } - catch (Exception ex) - { - throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex); - } - } - - public override object Get(DbDataReader rs, string name, ISessionImplementor session) - { - return Get(rs, rs.GetOrdinal(name), session); - } + /// + public override string Name => "Date"; - public override System.Type ReturnedClass - { - get { return typeof(DateTime); } - } - - public override void Set(DbCommand st, object value, int index, ISessionImplementor session) - { - var parm = st.Parameters[index]; - var dateTime = (DateTime)value; - parm.Value = dateTime.Date; - } + /// + protected override DateTime AdjustDateTime(DateTime dateValue) => + dateValue.Date; + /// public override bool IsEqual(object x, object y) { if (x == y) @@ -78,20 +48,21 @@ public override bool IsEqual(object x, object y) return false; } - DateTime date1 = (DateTime)x; - DateTime date2 = (DateTime)y; + var date1 = (DateTime)x; + var date2 = (DateTime)y; if (date1.Equals(date2)) return true; return date1.Day == date2.Day - && date1.Month == date2.Month - && date1.Year == date2.Year; + && date1.Month == date2.Month + && date1.Year == date2.Year; } + /// public override int GetHashCode(object x) { - DateTime date = (DateTime)x; - int hashCode = 1; + var date = (DateTime)x; + var hashCode = 1; unchecked { hashCode = 31 * hashCode + date.Day; @@ -101,35 +72,16 @@ public override int GetHashCode(object x) return hashCode; } - public override string ToString(object val) - { - return ((DateTime) val).ToShortDateString(); - } - - public override object FromStringValue(string xml) - { - return DateTime.Parse(xml); - } - - public object StringToObject(string xml) - { - return string.IsNullOrEmpty(xml) ? null : FromStringValue(xml); - } - - public override System.Type PrimitiveClass - { - get { return typeof(DateTime); } - } + /// + public override string ToString(object val) => + ((DateTime) val).ToShortDateString(); - public override object DefaultValue - { - get { return customBaseDate; } - } + /// + public override object DefaultValue => customBaseDate; - public override string ObjectToSQLString(object value, Dialect.Dialect dialect) - { - return "\'" + ((DateTime)value).ToShortDateString() + "\'"; - } + /// + public override string ObjectToSQLString(object value, Dialect.Dialect dialect) => + "\'" + ((DateTime)value).ToShortDateString() + "\'"; // Since v5 [Obsolete("Its only parameter, BaseValue, is obsolete.")] diff --git a/src/NHibernate/Type/DbTimestampType.cs b/src/NHibernate/Type/DbTimestampType.cs index 0761ae1facb..4eb9118c810 100644 --- a/src/NHibernate/Type/DbTimestampType.cs +++ b/src/NHibernate/Type/DbTimestampType.cs @@ -1,7 +1,6 @@ using System; using System.Data; using System.Data.Common; - using NHibernate.Engine; using NHibernate.Exceptions; using NHibernate.Impl; @@ -10,87 +9,84 @@ namespace NHibernate.Type { - /// An extension of which - /// maps to the database's current timestamp, rather than the vm's - /// current timestamp. + /// + /// When used as a version, gets seeded and incremented by querying the database's + /// current timestamp, rather than the application host's current timestamp. /// - /// - /// Note: May/may-not cause issues on dialects which do not properly support - /// a true notion of timestamp - /// [Serializable] - public partial class DbTimestampType : TimestampType, IVersionType + public partial class DbTimestampType : AbstractDateTimeType { - private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof (DbTimestampType)); + private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(DbTimestampType)); private static readonly SqlType[] EmptyParams = new SqlType[0]; - public override string Name - { - get { return "DbTimestamp"; } - } + /// + public override string Name => "DbTimestamp"; + /// public override object Seed(ISessionImplementor session) { if (session == null) { - log.Debug("incoming session was null; using current vm time"); - return base.Seed(session); + log.Debug("incoming session was null; using current application host time"); + return base.Seed(null); } - else if (!session.Factory.Dialect.SupportsCurrentTimestampSelection) + if (!session.Factory.Dialect.SupportsCurrentTimestampSelection) { - log.Debug("falling back to vm-based timestamp, as dialect does not support current timestamp selection"); + log.Info("falling back to application host based timestamp, as dialect does not support current timestamp selection"); return base.Seed(session); } - else - { - return GetCurrentTimestamp(session); - } + return GetCurrentTimestamp(session); } - private object GetCurrentTimestamp(ISessionImplementor session) + protected virtual DateTime GetCurrentTimestamp(ISessionImplementor session) { - Dialect.Dialect dialect = session.Factory.Dialect; - string timestampSelectString = dialect.CurrentTimestampSelectString; - return UsePreparedStatement(timestampSelectString, session); + var dialect = session.Factory.Dialect; + // Need to round notably for Sql Server DateTime with Odbc, which has a 3.33ms resolution, + // causing stale data update failure 2/3 of times if not rounded to 10ms. + return Round( + UsePreparedStatement(dialect.CurrentTimestampSelectString, session), + dialect.TimestampResolutionInTicks); } - protected virtual object UsePreparedStatement(string timestampSelectString, ISessionImplementor session) + protected virtual DateTime UsePreparedStatement(string timestampSelectString, ISessionImplementor session) { var tsSelect = new SqlString(timestampSelectString); DbCommand ps = null; DbDataReader rs = null; - using (new SessionIdLoggingContext(session.SessionId)) - try + using (new SessionIdLoggingContext(session.SessionId)) { - ps = session.Batcher.PrepareCommand(CommandType.Text, tsSelect, EmptyParams); - rs = session.Batcher.ExecuteReader(ps); - rs.Read(); - DateTime ts = rs.GetDateTime(0); - if (log.IsDebugEnabled) + try { - log.Debug("current timestamp retreived from db : " + ts + " (tiks=" + ts.Ticks + ")"); + ps = session.Batcher.PrepareCommand(CommandType.Text, tsSelect, EmptyParams); + rs = session.Batcher.ExecuteReader(ps); + rs.Read(); + var ts = rs.GetDateTime(0); + log.DebugFormat("current timestamp retreived from db : {0} (ticks={1})", ts, ts.Ticks); + return ts; } - return ts; - } - catch (DbException sqle) - { - throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, sqle, - "could not select current db timestamp", tsSelect); - } - finally - { - if (ps != null) + catch (DbException sqle) { - try - { - session.Batcher.CloseCommand(ps, rs); - } - catch (DbException sqle) + throw ADOExceptionHelper.Convert( + session.Factory.SQLExceptionConverter, + sqle, + "could not select current db timestamp", + tsSelect); + } + finally + { + if (ps != null) { - log.Warn("unable to clean up prepared statement", sqle); + try + { + session.Batcher.CloseCommand(ps, rs); + } + catch (DbException sqle) + { + log.Warn("unable to clean up prepared statement", sqle); + } } } } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/LocalDateTimeType.cs b/src/NHibernate/Type/LocalDateTimeType.cs index 2683b1c8865..51bfad3a55c 100644 --- a/src/NHibernate/Type/LocalDateTimeType.cs +++ b/src/NHibernate/Type/LocalDateTimeType.cs @@ -1,18 +1,40 @@ using System; +using NHibernate.SqlTypes; namespace NHibernate.Type { [Serializable] - public class LocalDateTimeType : AbstractDateTimeSpecificKindType + public class LocalDateTimeType : DateTimeType { - protected override DateTimeKind DateTimeKind + /// + /// Default constructor. + /// + public LocalDateTimeType() { - get { return DateTimeKind.Local; } } - public override string Name + /// + /// Constructor for specifying a datetime with a scale. Use . + /// + /// The sql type to use for the type. + public LocalDateTimeType(DateTimeSqlType sqlType) : base(sqlType) { - get { return "LocalDateTime"; } } + + /// + protected override DateTimeKind Kind => DateTimeKind.Local; + + /// + public override string Name => "LocalDateTime"; + } + + [Serializable] + public class LocalDateTimeNoMsType : DateTimeNoMsType + { + /// + protected override DateTimeKind Kind => DateTimeKind.Local; + + /// + public override string Name => "LocalDateTimeNoMs"; } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/NullableType.cs b/src/NHibernate/Type/NullableType.cs index aedf12485de..6903eb26af6 100644 --- a/src/NHibernate/Type/NullableType.cs +++ b/src/NHibernate/Type/NullableType.cs @@ -34,9 +34,9 @@ private IInternalLogger Log /// /// Initialize a new instance of the NullableType class using a - /// . + /// . /// - /// The underlying . + /// The underlying . /// This is used when the Property is mapped to a single column. protected NullableType(SqlType sqlType) { @@ -266,10 +266,10 @@ public sealed override object NullSafeGet(DbDataReader rs, string name, ISession } /// - /// Gets the underlying for + /// Gets the underlying for /// the column mapped by this . /// - /// The underlying . + /// The underlying . /// /// This implementation should be suitable for all subclasses unless they need to /// do some special things to get the value. There are no built in s @@ -291,9 +291,20 @@ public virtual SqlType SqlType /// column. All of their implementation should be in . /// /// - public override sealed SqlType[] SqlTypes(IMapping mapping) + public sealed override SqlType[] SqlTypes(IMapping mapping) { - return new SqlType[] {SqlType}; + return new[] { OverrideSqlType(mapping, SqlType) }; + } + + /// + /// Overrides the sql type. + /// + /// The type to override. + /// The mapping for which to override . + /// The refined types. + static SqlType OverrideSqlType(IMapping mapping, SqlType type) + { + return mapping != null ? mapping.Dialect.OverrideSqlType(type) : type; } /// diff --git a/src/NHibernate/Type/OneToOneType.cs b/src/NHibernate/Type/OneToOneType.cs index 2d8607a864f..c903be941fe 100644 --- a/src/NHibernate/Type/OneToOneType.cs +++ b/src/NHibernate/Type/OneToOneType.cs @@ -24,7 +24,7 @@ public override int GetColumnSpan(IMapping session) return 0; } - public override SqlType[] SqlTypes(IMapping session) + public override SqlType[] SqlTypes(IMapping mapping) { return NoSqlTypes; } @@ -154,4 +154,4 @@ public override bool[] ToColumnNullness(object value, IMapping mapping) return ArrayHelper.EmptyBoolArray; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/TimeAsTimeSpanType.cs b/src/NHibernate/Type/TimeAsTimeSpanType.cs index 0c4b067400d..a946f9bef3a 100644 --- a/src/NHibernate/Type/TimeAsTimeSpanType.cs +++ b/src/NHibernate/Type/TimeAsTimeSpanType.cs @@ -18,8 +18,18 @@ public partial class TimeAsTimeSpanType : PrimitiveType, IVersionType { private static readonly DateTime BaseDateValue = new DateTime(1753, 01, 01); - public TimeAsTimeSpanType() - : base(SqlTypeFactory.Time) + /// + /// Default constructor. + /// + public TimeAsTimeSpanType() : base(SqlTypeFactory.Time) + { + } + + /// + /// Constructor for specifying a time with a scale. Use . + /// + /// The sql type to use for the type. + public TimeAsTimeSpanType(TimeSqlType sqlType) : base(sqlType) { } @@ -111,4 +121,4 @@ public override string ObjectToSQLString(object value, Dialect.Dialect dialect) return '\'' + ((TimeSpan)value).Ticks.ToString() + '\''; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/TimeType.cs b/src/NHibernate/Type/TimeType.cs index 75cd303899b..e3e85d44f66 100644 --- a/src/NHibernate/Type/TimeType.cs +++ b/src/NHibernate/Type/TimeType.cs @@ -28,10 +28,21 @@ public class TimeType : PrimitiveType, IIdentifierType, ILiteralType { private static readonly DateTime BaseDateValue = new DateTime(1753, 01, 01); + /// + /// Default constructor. + /// public TimeType() : base(SqlTypeFactory.Time) { } + /// + /// Constructor for specifying a time with a scale. Use . + /// + /// The sql type to use for the type. + public TimeType(TimeSqlType sqlType) : base(sqlType) + { + } + public override string Name { get { return "Time"; } @@ -47,7 +58,7 @@ public override object Get(DbDataReader rs, int index, ISessionImplementor sessi } DateTime dbValue = Convert.ToDateTime(rs[index]); - return new DateTime(1753, 01, 01, dbValue.Hour, dbValue.Minute, dbValue.Second); + return BaseDateValue.Add(dbValue.TimeOfDay); } catch (Exception ex) { diff --git a/src/NHibernate/Type/TimestampType.cs b/src/NHibernate/Type/TimestampType.cs index d133dad3c0a..e15f3afe0b7 100644 --- a/src/NHibernate/Type/TimestampType.cs +++ b/src/NHibernate/Type/TimestampType.cs @@ -1,142 +1,45 @@ using System; -using System.Collections; -using System.Data.Common; -using NHibernate.Engine; -using NHibernate.SqlTypes; namespace NHibernate.Type { + // Obsolete since v5.0 /// - /// This is almost the exact same type as the DateTime except it can be used - /// in the version column, stores it to the accuracy the database supports, - /// and will default to the value of DateTime.Now if the value is null. + /// This is almost the exact same type as the . /// /// - ///

+ /// /// The value stored in the database depends on what your data provider is capable - /// of storing. So there is a possibility that the DateTime you save will not be - /// the same DateTime you get back when you check DateTime.Equals(DateTime) because + /// of storing. So there is a possibility that the DateTime you save will not be + /// the same DateTime you get back when you check because /// they will have their milliseconds off. - ///

- ///

- /// For example - SQL Server 2000 is only accurate to 3.33 milliseconds. So if + /// + /// + /// For example - SQL Server 2000 is only accurate to 3.33 milliseconds. So if /// NHibernate writes a value of 01/01/98 23:59:59.995 to the Prepared Command, MsSql /// will store it as 1998-01-01 23:59:59.997. - ///

- ///

+ /// + /// /// Please review the documentation of your Database server. - ///

+ /// + /// + /// If you are looking for the most accurate date and time storage accross databases use the + /// . + /// ///
+ [Obsolete("Please use DateTimeType instead.")] [Serializable] - public partial class TimestampType : PrimitiveType, IVersionType, ILiteralType + public class TimestampType : AbstractDateTimeType { - /// - /// Retrieve the current system Local time. - /// - /// DateTime.Now - protected virtual DateTime Now => DateTime.Now; + /// + public override string Name => "Timestamp"; /// - /// Returns the DateTimeKind for type . + /// Retrieve the string representation of the timestamp object. This is in the following format: + /// + /// 2011-01-27T14:50:59.6220000Z + /// /// - /// Returns DateTimeKind.Unspecified - protected virtual DateTimeKind Kind => DateTimeKind.Unspecified; - - public TimestampType() : base(SqlTypeFactory.DateTime) - { - } - - public override object Get(DbDataReader rs, int index, ISessionImplementor session) - { - return DateTime.SpecifyKind(Convert.ToDateTime(rs[index]), Kind); - } - - public override object Get(DbDataReader rs, string name, ISessionImplementor session) - { - return Get(rs, rs.GetOrdinal(name), session); - } - - public override System.Type ReturnedClass - { - get { return typeof(DateTime); } - } - - /// - /// Sets the value of this Type in the DbCommand. - /// - /// The DbCommand to add the Type's value to. - /// The value of the Type. - /// The index of the DbParameter in the DbCommand. - /// The session for which the operation is done. - /// - /// No null values will be written to the DbCommand for this Type. - /// - public override void Set(DbCommand st, object value, int index, ISessionImplementor session) - { - st.Parameters[index].Value = (value is DateTime) ? value : DateTime.SpecifyKind(Now, Kind); - } - - public override string Name - { - get { return "Timestamp"; } - } - - public override string ToString(object val) - { - return ((DateTime) val).ToString("O"); - } - - public override object FromStringValue(string xml) - { - return DateTime.Parse(xml); - } - - #region IVersionType Members - - public object Next(object current, ISessionImplementor session) - { - return Seed(session); - } - - public static DateTime Round(DateTime value, long resolution) - { - return value.AddTicks(-(value.Ticks % resolution)); - } - - public virtual object Seed(ISessionImplementor session) - { - if (session == null) - { - return Now; - } - return Round(Now, session.Factory.Dialect.TimestampResolutionInTicks); - } - - public IComparer Comparator - { - get { return Comparer.DefaultInvariant; } - } - - #endregion - - public object StringToObject(string xml) - { - return FromStringValue(xml); - } - - public override System.Type PrimitiveClass - { - get { return typeof(DateTime); } - } - - public override object DefaultValue - { - get { return DateTime.MinValue; } - } - - public override string ObjectToSQLString(object value, Dialect.Dialect dialect) - { - return '\'' + value.ToString() + '\''; - } + public override string ToString(object val) => + ((DateTime) val).ToString("O"); } } diff --git a/src/NHibernate/Type/TimestampUtcType.cs b/src/NHibernate/Type/TimestampUtcType.cs deleted file mode 100644 index f2ea6f66b82..00000000000 --- a/src/NHibernate/Type/TimestampUtcType.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Data.Common; -using NHibernate.Engine; - -namespace NHibernate.Type -{ - [Serializable] - public class TimestampUtcType : TimestampType - { - /// - /// Returns the DateTimeKind for type . - /// - /// Returns DateTimeKind.Utc - protected override DateTimeKind Kind => DateTimeKind.Utc; - - /// - /// Retrieve the current system Utc time. - /// - /// DateTime.UtcNow - protected override DateTime Now => DateTime.UtcNow; - - public override string Name => "TimestampUtc"; - - /// - /// Parse the value by the base implementation and convert it to Utc as the .net framework by default parse to a DateTime value with Kind set to Local. - /// - /// DateTime value where Kind is Utc - public override object FromStringValue(string xml) - { - return ((DateTime) base.FromStringValue(xml)).ToUniversalTime(); - } - - /// - /// Validate the passed DateTime value if Kind is set to Utc and passes value to base implementation (). - /// - /// Thrown when Kind is NOT Utc. - public override void Set(DbCommand st, object value, int index, ISessionImplementor session) - { - if (value is DateTime) - { - var v = (DateTime) value; - if (v.Kind != DateTimeKind.Utc) throw new ArgumentException("Kind is NOT Utc", nameof(value)); - } - - base.Set(st, value, index, session); - } - - /// - /// Compares two DateTime object and also compare its Kind which is not used by the .net framework DateTime.Equals implementation. - /// - /// - /// - /// - public override bool IsEqual(object x, object y) - { - return base.IsEqual(x, y) && ((DateTime) x).Kind == ((DateTime) y).Kind; - } - - /// - /// Retrieve the string representation of the timestamp object. This is in the following format: - /// - /// 2011-01-27T14:50:59.6220000Z - /// - /// - public override string ToString(object val) - { - return ((DateTime) val).ToString("o"); - } - } -} diff --git a/src/NHibernate/Type/TypeFactory.cs b/src/NHibernate/Type/TypeFactory.cs index 6695fa2daa1..e7a866ac5fa 100644 --- a/src/NHibernate/Type/TypeFactory.cs +++ b/src/NHibernate/Type/TypeFactory.cs @@ -8,7 +8,6 @@ using System.Xml.Linq; using NHibernate.Bytecode; using NHibernate.Classic; -using NHibernate.Linq; using NHibernate.SqlTypes; using NHibernate.UserTypes; using NHibernate.Util; @@ -30,10 +29,11 @@ public sealed class TypeFactory private enum TypeClassification { Plain, - Length, + LengthOrScale, PrecisionScale } - + + private static readonly IInternalLogger _log = LoggerProvider.LoggerFor(typeof(TypeFactory)); private static readonly string[] EmptyAliases= new string[0]; private static readonly char[] PrecisionScaleSplit = new[] { '(', ')', ',' }; private static readonly char[] LengthSplit = new[] { '(', ')' }; @@ -81,13 +81,16 @@ private enum TypeClassification private static readonly ConcurrentDictionary typeByTypeOfName = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary getTypeDelegatesWithLength = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _obsoleteMessageByAlias = + new ConcurrentDictionary(); + + private static readonly ConcurrentDictionary _getTypeDelegatesWithLengthOrScale = + new ConcurrentDictionary(); private static readonly ConcurrentDictionary getTypeDelegatesWithPrecision = new ConcurrentDictionary(); - private delegate NullableType GetNullableTypeWithLength(int length); // Func + private delegate NullableType GetNullableTypeWithLengthOrScale(int lengthOrScale); // Func private delegate NullableType GetNullableTypeWithPrecision(byte precision, byte scale); @@ -102,12 +105,12 @@ private static void RegisterType(System.Type systemType, IType nhibernateType, I } private static void RegisterType(System.Type systemType, IType nhibernateType, - IEnumerable aliases, GetNullableTypeWithLength ctorLength) + IEnumerable aliases, GetNullableTypeWithLengthOrScale ctorLengthOrScale) { var typeAliases = new List(aliases); typeAliases.AddRange(GetClrTypeAliases(systemType)); - RegisterType(nhibernateType, typeAliases, ctorLength); + RegisterType(nhibernateType, typeAliases, ctorLengthOrScale); } private static void RegisterType(System.Type systemType, IType nhibernateType, @@ -141,17 +144,17 @@ private static void RegisterType(IType nhibernateType, IEnumerable alias var typeAliases = new List(aliases) { nhibernateType.Name }; foreach (var alias in typeAliases) { - typeByTypeOfName[alias] = nhibernateType; + RegisterTypeAlias(nhibernateType, alias); } } - private static void RegisterType(IType nhibernateType, IEnumerable aliases, GetNullableTypeWithLength ctorLength) + private static void RegisterType(IType nhibernateType, IEnumerable aliases, GetNullableTypeWithLengthOrScale ctorLengthOrScale) { var typeAliases = new List(aliases) { nhibernateType.Name }; foreach (var alias in typeAliases) { - typeByTypeOfName[alias] = nhibernateType; - if (!getTypeDelegatesWithLength.TryAdd(alias, ctorLength)) + RegisterTypeAlias(nhibernateType, alias); + if (!_getTypeDelegatesWithLengthOrScale.TryAdd(alias, ctorLengthOrScale)) { throw new HibernateException("An item with the same key has already been added to getTypeDelegatesWithLength."); } @@ -163,7 +166,7 @@ private static void RegisterType(IType nhibernateType, IEnumerable alias var typeAliases = new List(aliases) { nhibernateType.Name }; foreach (var alias in typeAliases) { - typeByTypeOfName[alias] = nhibernateType; + RegisterTypeAlias(nhibernateType, alias); if (!getTypeDelegatesWithPrecision.TryAdd(alias, ctorPrecision)) { throw new HibernateException("An item with the same key has already been added to getTypeDelegatesWithPrecision."); @@ -171,6 +174,23 @@ private static void RegisterType(IType nhibernateType, IEnumerable alias } } + private static void RegisterTypeAlias(IType nhibernateType, string alias) + { + typeByTypeOfName[alias] = nhibernateType; + // Ignore obsolete search for aliases which are to be remapped to other types. + switch (alias) + { + case "timestamp": + case "Timestamp": + return; + } + var obsolete = nhibernateType.GetType().GetCustomAttribute(false); + if (obsolete != null) + { + _obsoleteMessageByAlias[alias] = obsolete.Message; + } + } + /// static TypeFactory() { @@ -199,8 +219,10 @@ private static void RegisterDefaultNetTypes() RegisterType(typeof (Byte), NHibernateUtil.Byte, new[]{ "byte"}); RegisterType(typeof (Char), NHibernateUtil.Character, new[] {"character", "char"}); RegisterType(typeof (CultureInfo), NHibernateUtil.CultureInfo, new[]{ "locale"}); - RegisterType(typeof (DateTime), NHibernateUtil.DateTime, new[]{ "datetime"} ); - RegisterType(typeof (DateTimeOffset), NHibernateUtil.DateTimeOffset, new[]{ "datetimeoffset"}); + RegisterType(typeof(DateTime), NHibernateUtil.DateTime, new[] { "datetime" }, + s => GetType(NHibernateUtil.DateTime, s, scale => new DateTimeType(SqlTypeFactory.GetDateTime((byte)scale)))); + RegisterType(typeof (DateTimeOffset), NHibernateUtil.DateTimeOffset, new[]{ "datetimeoffset"}, + s => GetType(NHibernateUtil.DateTimeOffset, s, scale => new DateTimeOffsetType(SqlTypeFactory.GetDateTimeOffset((byte)scale)))); RegisterType(typeof (Decimal), NHibernateUtil.Decimal, new[] {"big_decimal", "decimal"}, (p, s) => GetType(NHibernateUtil.Decimal, p, s, st => new DecimalType(st))); @@ -259,23 +281,34 @@ private static void RegisterBuiltInTypes() RegisterType(NHibernateUtil.StringClob, EmptyAliases, l => GetType(NHibernateUtil.StringClob, l, len => new StringClobType(SqlTypeFactory.GetStringClob(len)))); - + + RegisterType(NHibernateUtil.DateTimeNoMs, new[] { "datetimenoms" }); RegisterType(NHibernateUtil.Date, new[] { "date" }); +#pragma warning disable 618 // Timestamp is obsolete RegisterType(NHibernateUtil.Timestamp, new[] { "timestamp" }); - RegisterType(NHibernateUtil.TimestampUtc, new[] { "timestamputc" }); +#pragma warning restore 618 RegisterType(NHibernateUtil.DbTimestamp, new[] { "dbtimestamp" }); - RegisterType(NHibernateUtil.Time, new[] { "time" }); + RegisterType(NHibernateUtil.Time, new[] { "time" }, + s => GetType(NHibernateUtil.Time, s, scale => new TimeType(SqlTypeFactory.GetTime((byte)scale)))); RegisterType(NHibernateUtil.TrueFalse, new[] { "true_false" }); RegisterType(NHibernateUtil.YesNo, new[] { "yes_no" }); RegisterType(NHibernateUtil.Ticks, new[] { "ticks" }); - RegisterType(NHibernateUtil.TimeAsTimeSpan, EmptyAliases); - RegisterType(NHibernateUtil.LocalDateTime, new[] { "localdatetime" }); - RegisterType(NHibernateUtil.UtcDateTime, new[] { "utcdatetime" }); - + RegisterType(NHibernateUtil.TimeAsTimeSpan, new[] { "timeastimespan" }, + s => GetType(NHibernateUtil.TimeAsTimeSpan, s, scale => new TimeAsTimeSpanType(SqlTypeFactory.GetTime((byte)scale)))); + RegisterType(NHibernateUtil.LocalDateTime, new[] { "localdatetime" }, + s => GetType(NHibernateUtil.LocalDateTime, s, scale => new LocalDateTimeType(SqlTypeFactory.GetDateTime((byte)scale)))); + RegisterType(NHibernateUtil.UtcDateTime, new[] { "utcdatetime" }, + s => GetType(NHibernateUtil.UtcDateTime, s, scale => new UtcDateTimeType(SqlTypeFactory.GetDateTime((byte)scale)))); + RegisterType(NHibernateUtil.LocalDateTimeNoMs, new[] { "localdatetimenoms" }); + RegisterType(NHibernateUtil.UtcDateTimeNoMs, new[] { "utcdatetimenoms" }); + RegisterType(NHibernateUtil.Currency, new[] { "currency" }, (p, s) => GetType(NHibernateUtil.Currency, p, s, st => new CurrencyType(st))); - - RegisterType(NHibernateUtil.DateTime2, new[] { "datetime2" }); + +#pragma warning disable 618 // DateTime2 is obsolete + RegisterType(NHibernateUtil.DateTime2, new[] { "datetime2" }, + s => GetType(NHibernateUtil.DateTime2, s, scale => new DateTime2Type(SqlTypeFactory.GetDateTime2((byte)scale)))); +#pragma warning restore 618 RegisterType(NHibernateUtil.Serializable, new[] {"Serializable", "serializable"}, l => GetType(NHibernateUtil.Serializable, l, @@ -331,7 +364,7 @@ private static TypeClassification GetTypeClassification(string typeName) } else { - return TypeClassification.Length; + return TypeClassification.LengthOrScale; } } else @@ -359,6 +392,8 @@ public static IType Basic(string name) IType returnType; if (typeByTypeOfName.TryGetValue(name, out returnType)) { + if (_obsoleteMessageByAlias.TryGetValue(name, out string obsoleteMessage)) + _log.WarnFormat("{0} is obsolete. {1}", name, obsoleteMessage); return returnType; } @@ -384,14 +419,14 @@ public static IType Basic(string name) return BuiltInType(typeName, precision, scale); } - else if (typeClassification == TypeClassification.Length) + else if (typeClassification == TypeClassification.LengthOrScale) { - //length based + //length or scale based string[] parsedName = name.Split(LengthSplit); if (parsedName.Length < 3) { - throw new ArgumentOutOfRangeException("TypeClassification.Length", name, "It is not a valid Length name"); + throw new ArgumentOutOfRangeException("TypeClassification.LengthOrScale", name, "It is not a valid Length or Scale name"); } typeName = parsedName[0].Trim(); @@ -410,11 +445,11 @@ public static IType Basic(string name) } } - internal static IType BuiltInType(string typeName, int length) + internal static IType BuiltInType(string typeName, int lengthOrScale) { - GetNullableTypeWithLength lengthDelegate; + GetNullableTypeWithLengthOrScale lengthOrScaleDelegate; - return !getTypeDelegatesWithLength.TryGetValue(typeName, out lengthDelegate) ? null : lengthDelegate(length); + return !_getTypeDelegatesWithLengthOrScale.TryGetValue(typeName, out lengthOrScaleDelegate) ? null : lengthOrScaleDelegate(lengthOrScale); } internal static IType BuiltInType(string typeName, byte precision, byte scale) @@ -453,9 +488,9 @@ private static void AddToTypeOfNameWithPrecision(string key, IType type) } } - private static string GetKeyForLengthBased(string name, int length) + private static string GetKeyForLengthOrScaleBased(string name, int lengthOrScale) { - return name + "(" + length + ")"; + return name + "(" + lengthOrScale + ")"; } private static string GetKeyForPrecisionScaleBased(string name, byte precision, byte scale) @@ -509,7 +544,7 @@ public static IType HeuristicType(string typeName, IDictionary p string[] parsedTypeName; TypeClassification typeClassification = GetTypeClassification(typeName); - if (typeClassification == TypeClassification.Length) + if (typeClassification == TypeClassification.LengthOrScale) parsedTypeName = typeName.Split(LengthSplit); else parsedTypeName = typeClassification == TypeClassification.PrecisionScale ? typeName.Split(PrecisionScaleSplit) : new[] { typeName }; @@ -539,6 +574,12 @@ public static IType HeuristicType(string typeName, IDictionary p throw new MappingException("Could not instantiate IType " + typeClass.Name + ": " + e, e); } InjectParameters(type, parameters); + + var obsolete = typeClass.GetCustomAttribute(false); + if (obsolete != null) + { + _log.WarnFormat("{0} is obsolete. {1}", typeName, obsolete.Message); + } return type; } if (typeof(ICompositeUserType).IsAssignableFrom(typeClass)) @@ -563,7 +604,7 @@ public static IType HeuristicType(string typeName, IDictionary p if (!typeClass.IsSerializable) return null; - if (typeClassification == TypeClassification.Length) + if (typeClassification == TypeClassification.LengthOrScale) return GetSerializableType(typeClass, Int32.Parse(parsedTypeName[1])); if (length.HasValue) @@ -576,7 +617,7 @@ public static IType HeuristicType(string typeName, IDictionary p [MethodImpl(MethodImplOptions.Synchronized)] public static NullableType GetAnsiStringType(int length) { - string key = GetKeyForLengthBased(NHibernateUtil.AnsiString.Name, length); + string key = GetKeyForLengthOrScaleBased(NHibernateUtil.AnsiString.Name, length); IType returnType; if (!typeByTypeOfName.TryGetValue(key, out returnType)) @@ -606,7 +647,7 @@ public static NullableType GetBinaryType(int length) return NHibernateUtil.Binary; } - string key = GetKeyForLengthBased(NHibernateUtil.Binary.Name, length); + string key = GetKeyForLengthOrScaleBased(NHibernateUtil.Binary.Name, length); IType returnType; if (!typeByTypeOfName.TryGetValue(key, out returnType)) { @@ -618,13 +659,13 @@ public static NullableType GetBinaryType(int length) } [MethodImpl(MethodImplOptions.Synchronized)] - private static NullableType GetType(NullableType defaultUnqualifiedType, int length, GetNullableTypeWithLength ctorDelegate) + private static NullableType GetType(NullableType defaultUnqualifiedType, int lengthOrScale, GetNullableTypeWithLengthOrScale ctorDelegate) { - string key = GetKeyForLengthBased(defaultUnqualifiedType.Name, length); + string key = GetKeyForLengthOrScaleBased(defaultUnqualifiedType.Name, lengthOrScale); IType returnType; if (!typeByTypeOfName.TryGetValue(key, out returnType)) { - returnType = ctorDelegate(length); + returnType = ctorDelegate(lengthOrScale); AddToTypeOfNameWithLength(key, returnType); } @@ -682,7 +723,7 @@ public static NullableType GetSerializableType(System.Type serializableType) [MethodImpl(MethodImplOptions.Synchronized)] public static NullableType GetSerializableType(System.Type serializableType, int length) { - string key = GetKeyForLengthBased(serializableType.AssemblyQualifiedName, length); + string key = GetKeyForLengthOrScaleBased(serializableType.AssemblyQualifiedName, length); IType returnType; if (!typeByTypeOfName.TryGetValue(key, out returnType)) @@ -697,7 +738,7 @@ public static NullableType GetSerializableType(System.Type serializableType, int [MethodImpl(MethodImplOptions.Synchronized)] public static NullableType GetSerializableType(int length) { - string key = GetKeyForLengthBased(NHibernateUtil.Serializable.Name, length); + string key = GetKeyForLengthOrScaleBased(NHibernateUtil.Serializable.Name, length); IType returnType; if (!typeByTypeOfName.TryGetValue(key, out returnType)) @@ -712,7 +753,7 @@ public static NullableType GetSerializableType(int length) [MethodImpl(MethodImplOptions.Synchronized)] public static NullableType GetStringType(int length) { - string key = GetKeyForLengthBased(NHibernateUtil.String.Name, length); + string key = GetKeyForLengthOrScaleBased(NHibernateUtil.String.Name, length); IType returnType; if (!typeByTypeOfName.TryGetValue(key, out returnType)) @@ -727,7 +768,7 @@ public static NullableType GetStringType(int length) [MethodImpl(MethodImplOptions.Synchronized)] public static NullableType GetTypeType(int length) { - string key = GetKeyForLengthBased(typeof(TypeType).FullName, length); + string key = GetKeyForLengthOrScaleBased(typeof(TypeType).FullName, length); IType returnType; if (!typeByTypeOfName.TryGetValue(key, out returnType)) @@ -739,6 +780,140 @@ public static NullableType GetTypeType(int length) return (NullableType)returnType; } + /// + /// Gets a with desired fractional seconds precision. + /// + /// The fractional seconds precision. + /// The NHibernate type. + [MethodImpl(MethodImplOptions.Synchronized)] + public static NullableType GetDateTimeType(byte fractionalSecondsPrecision) + { + var key = GetKeyForLengthOrScaleBased(NHibernateUtil.DateTime.Name, fractionalSecondsPrecision); + + if (!typeByTypeOfName.TryGetValue(key, out var returnType)) + { + returnType = new DateTimeType(SqlTypeFactory.GetDateTime(fractionalSecondsPrecision)); + AddToTypeOfNameWithLength(key, returnType); + } + + return (NullableType)returnType; + } + + /// + /// Gets a with desired fractional seconds precision. + /// + /// The fractional seconds precision. + /// The NHibernate type. + [MethodImpl(MethodImplOptions.Synchronized)] + [Obsolete("Use GetDateTimeType instead, it uses DateTime2 with dialects supporting it.")] + public static NullableType GetDateTime2Type(byte fractionalSecondsPrecision) + { + var key = GetKeyForLengthOrScaleBased(NHibernateUtil.DateTime2.Name, fractionalSecondsPrecision); + + if (!typeByTypeOfName.TryGetValue(key, out var returnType)) + { + returnType = new DateTime2Type(SqlTypeFactory.GetDateTime2(fractionalSecondsPrecision)); + AddToTypeOfNameWithLength(key, returnType); + } + + return (NullableType)returnType; + } + + /// + /// Gets a with desired fractional seconds precision. + /// + /// The fractional seconds precision. + /// The NHibernate type. + [MethodImpl(MethodImplOptions.Synchronized)] + public static NullableType GetLocalDateTimeType(byte fractionalSecondsPrecision) + { + var key = GetKeyForLengthOrScaleBased(NHibernateUtil.LocalDateTime.Name, fractionalSecondsPrecision); + + if (!typeByTypeOfName.TryGetValue(key, out var returnType)) + { + returnType = new LocalDateTimeType(SqlTypeFactory.GetDateTime(fractionalSecondsPrecision)); + AddToTypeOfNameWithLength(key, returnType); + } + + return (NullableType)returnType; + } + + /// + /// Gets a with desired fractional seconds precision. + /// + /// The fractional seconds precision. + /// The NHibernate type. + [MethodImpl(MethodImplOptions.Synchronized)] + public static NullableType GetUtcDateTimeType(byte fractionalSecondsPrecision) + { + var key = GetKeyForLengthOrScaleBased(NHibernateUtil.UtcDateTime.Name, fractionalSecondsPrecision); + + if (!typeByTypeOfName.TryGetValue(key, out var returnType)) + { + returnType = new UtcDateTimeType(SqlTypeFactory.GetDateTime(fractionalSecondsPrecision)); + AddToTypeOfNameWithLength(key, returnType); + } + + return (NullableType)returnType; + } + + /// + /// Gets a with desired fractional seconds precision. + /// + /// The fractional seconds precision. + /// The NHibernate type. + [MethodImpl(MethodImplOptions.Synchronized)] + public static NullableType GetDateTimeOffsetType(byte fractionalSecondsPrecision) + { + var key = GetKeyForLengthOrScaleBased(NHibernateUtil.DateTimeOffset.Name, fractionalSecondsPrecision); + + if (!typeByTypeOfName.TryGetValue(key, out var returnType)) + { + returnType = new DateTimeOffsetType(SqlTypeFactory.GetDateTimeOffset(fractionalSecondsPrecision)); + AddToTypeOfNameWithLength(key, returnType); + } + + return (NullableType)returnType; + } + + /// + /// Gets a with desired fractional seconds precision. + /// + /// The fractional seconds precision. + /// The NHibernate type. + [MethodImpl(MethodImplOptions.Synchronized)] + public static NullableType GetTimeAsTimeSpanType(byte fractionalSecondsPrecision) + { + var key = GetKeyForLengthOrScaleBased(NHibernateUtil.TimeAsTimeSpan.Name, fractionalSecondsPrecision); + + if (!typeByTypeOfName.TryGetValue(key, out var returnType)) + { + returnType = new TimeAsTimeSpanType(SqlTypeFactory.GetTime(fractionalSecondsPrecision)); + AddToTypeOfNameWithLength(key, returnType); + } + + return (NullableType)returnType; + } + + /// + /// Gets a with desired fractional seconds precision. + /// + /// The fractional seconds precision. + /// The NHibernate type. + [MethodImpl(MethodImplOptions.Synchronized)] + public static NullableType GetTimeType(byte fractionalSecondsPrecision) + { + var key = GetKeyForLengthOrScaleBased(NHibernateUtil.Time.Name, fractionalSecondsPrecision); + + if (!typeByTypeOfName.TryGetValue(key, out var returnType)) + { + returnType = new TimeType(SqlTypeFactory.GetTime(fractionalSecondsPrecision)); + AddToTypeOfNameWithLength(key, returnType); + } + + return (NullableType)returnType; + } + // Association Types /// diff --git a/src/NHibernate/Type/TypeType.cs b/src/NHibernate/Type/TypeType.cs index ffb67a43bc4..263bb66b45b 100644 --- a/src/NHibernate/Type/TypeType.cs +++ b/src/NHibernate/Type/TypeType.cs @@ -20,9 +20,9 @@ internal TypeType() /// /// Initialize a new instance of the TypeType class using a - /// . + /// . /// - /// The underlying . + /// The underlying . internal TypeType(StringSqlType sqlType) : base(sqlType) {} diff --git a/src/NHibernate/Type/UtcDateTimeType.cs b/src/NHibernate/Type/UtcDateTimeType.cs index a9b35c87b0d..9c40d188d75 100644 --- a/src/NHibernate/Type/UtcDateTimeType.cs +++ b/src/NHibernate/Type/UtcDateTimeType.cs @@ -1,18 +1,40 @@ using System; +using NHibernate.SqlTypes; namespace NHibernate.Type { [Serializable] - public class UtcDateTimeType : AbstractDateTimeSpecificKindType + public class UtcDateTimeType : DateTimeType { - protected override DateTimeKind DateTimeKind + /// + /// Default constructor. + /// + public UtcDateTimeType() { - get { return DateTimeKind.Utc; } } - public override string Name + /// + /// Constructor for specifying a datetime with a scale. Use . + /// + /// The sql type to use for the type. + public UtcDateTimeType(DateTimeSqlType sqlType) : base(sqlType) { - get { return "UtcDateTime"; } } + + /// + protected override DateTimeKind Kind => DateTimeKind.Utc; + + /// + public override string Name => "UtcDateTime"; + } + + [Serializable] + public class UtcDateTimeNoMsType : DateTimeNoMsType + { + /// + protected override DateTimeKind Kind => DateTimeKind.Utc; + + /// + public override string Name => "UtcDateTimeNoMs"; } -} \ No newline at end of file +} diff --git a/src/NHibernate/nhibernate-configuration.xsd b/src/NHibernate/nhibernate-configuration.xsd index aa3a99e791a..7a837e28909 100644 --- a/src/NHibernate/nhibernate-configuration.xsd +++ b/src/NHibernate/nhibernate-configuration.xsd @@ -161,6 +161,14 @@ + + + + Disable switching built-in NHibernate date-time types from DbType.DateTime to DbType.DateTime2 + for dialects supporting datetime2. + + + diff --git a/src/NHibernate/nhibernate-mapping.xsd b/src/NHibernate/nhibernate-mapping.xsd index b6a47597057..fb25ab1bfb2 100644 --- a/src/NHibernate/nhibernate-mapping.xsd +++ b/src/NHibernate/nhibernate-mapping.xsd @@ -140,7 +140,6 @@ - @@ -1415,34 +1414,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - -