From 9bae654f544117f815b4f66f33c4496f3fba2505 Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Fri, 19 Apr 2024 01:18:12 +0200 Subject: [PATCH] Fix Linq .Second bug for PostgreSQL et al. Refactor tests --- .../Async/Linq/DateTimeTests.cs | 240 +++++++++++++---- src/NHibernate.Test/Linq/DateTimeTests.cs | 242 ++++++++++++++---- src/NHibernate/Dialect/FirebirdDialect.cs | 1 + src/NHibernate/Dialect/Oracle8iDialect.cs | 2 + src/NHibernate/Dialect/PostgreSQLDialect.cs | 2 + .../DateTimePropertiesHqlGenerator.cs | 12 +- 6 files changed, 409 insertions(+), 90 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/DateTimeTests.cs b/src/NHibernate.Test/Async/Linq/DateTimeTests.cs index 8d0fd7c1598..22afc511d2f 100644 --- a/src/NHibernate.Test/Async/Linq/DateTimeTests.cs +++ b/src/NHibernate.Test/Async/Linq/DateTimeTests.cs @@ -9,98 +9,252 @@ using System; +using System.Data; using System.Linq; +using NHibernate.Cfg; +using NHibernate.SqlTypes; +using NHibernate.Mapping.ByCode; using NUnit.Framework; +using NHibernate.Type; using NHibernate.Linq; +using System.Linq.Expressions; namespace NHibernate.Test.Linq { using System.Threading.Tasks; + using System.Threading; [TestFixture] - public class DateTimeTestsAsync : LinqTestCase + public class DateTimeTestsAsync : TestCase { + private bool DialectSupportsDateTimeOffset => TestDialect.SupportsSqlType(new SqlType(DbType.DateTimeOffset)); + private bool DialectSupportsDateTimeWithScale => TestDialect.SupportsSqlType(new SqlType(DbType.DateTime, 2)); + + private DateTimeTestsClass[] _referenceEntities = new DateTimeTestsClass[] + { + new() {Id =1, DateTimeValue = new DateTime(1998, 02, 26)}, + new() {Id =2, DateTimeValue = new DateTime(1998, 02, 26)}, + new() {Id =3, DateTimeValue = new DateTime(1998, 02, 26, 01, 01, 01)}, + new() {Id =4, DateTimeValue = new DateTime(1998, 02, 26, 02, 02, 02)}, + new() {Id =5, DateTimeValue = new DateTime(1998, 02, 26, 03, 03, 03)}, + new() {Id =6, DateTimeValue = new DateTime(1998, 02, 26, 04, 04, 04)}, + new() {Id =7, DateTimeValue = new DateTime(1998, 03, 01)}, + new() {Id =8, DateTimeValue = new DateTime(2000, 01, 01)} + }; + + protected override string[] Mappings => default; + protected override void AddMappings(Configuration configuration) + { + var modelMapper = new ModelMapper(); + + modelMapper.Class(m => + { + m.Table("datetimetests"); + m.Lazy(false); + m.Id(p => p.Id, p => p.Generator(Generators.Assigned)); + m.Property(p => p.DateValue, c => c.Type()); + m.Property(p => p.DateTimeValue); + m.Property(p => p.DateTimeValueWithScale, c => c.Scale(2)); + if (DialectSupportsDateTimeOffset) + { + m.Property(p => p.DateTimeOffsetValue); + m.Property(p => p.DateTimeOffsetValueWithScale, c => c.Scale(2)); + } + }); + var mapping = modelMapper.CompileMappingForAllExplicitlyAddedEntities(); + configuration.AddMapping(mapping); + } + + protected override void OnSetUp() + { + foreach (var entity in _referenceEntities) + { + entity.DateValue = entity.DateTimeValue.Date; + entity.DateTimeValueWithScale = entity.DateTimeValue.AddSeconds(0.9); + entity.DateTimeOffsetValue = new DateTimeOffset(entity.DateTimeValue, TimeSpan.FromHours(3)); + entity.DateTimeOffsetValueWithScale = new DateTimeOffset(entity.DateTimeValue, TimeSpan.FromHours(3)); + } + + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + foreach (var entity in _referenceEntities) + { + session.Save(entity); + } + trans.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + session.Query().Delete(); + trans.Commit(); + } + } + + private void AssertDateTimeOffsetSupported() + { + if (!DialectSupportsDateTimeOffset) + { + Assert.Ignore("Dialect doesn't support DateTimeOffset"); + } + } + + private void AssertDateTimeWithScaleSupported() + { + if (!DialectSupportsDateTimeWithScale) + { + Assert.Ignore("Dialect doesn't support DateTime with scale (2)"); + } + } + + private Task AssertQueryAsync(Expression> where, CancellationToken cancellationToken = default(CancellationToken)) => AssertQueryAsync(where, x => x.Id, cancellationToken); + + private async Task AssertQueryAsync(Expression> where, Expression> select, CancellationToken cancellationToken = default(CancellationToken)) + { + using var session = OpenSession(); + var fromDb = await (session.Query().Where(where).Select(select).ToListAsync(cancellationToken)); + var fromMemory = _referenceEntities.AsQueryable().Where(where).Select(select).AsEnumerable().ToList(); //AsEnumerable added to avoid async generator + Assert.That(fromMemory, Has.Count.GreaterThan(0), "Inconclusive, since the query doesn't match anything in the defined set"); + Assert.That(fromDb, Has.Count.EqualTo(fromMemory.Count)); + Assert.That(fromDb, Is.EquivalentTo(fromMemory)); + } + [Test] public async Task CanQueryByYearAsync() { - var x = await ((from o in db.Orders - where o.OrderDate.Value.Year == 1998 - select o).ToListAsync()); + await (AssertQueryAsync(o => o.DateTimeValue.Year == 1998)); + } - Assert.AreEqual(270, x.Count()); + [Test] + public async Task CanQueryDateTimeBySecondAsync() + { + await (AssertQueryAsync(o => o.DateTimeValue.Second == 4)); } [Test] - public async Task CanQueryByDateAsync() + public async Task CanQueryDateTimeByMinuteAsync() { - var x = await ((from o in db.Orders - where o.OrderDate.Value.Date == new DateTime(1998, 02, 26) - select o).ToListAsync()); + await (AssertQueryAsync(o => o.DateTimeValue.Minute == 4)); + } - Assert.AreEqual(6, x.Count()); + [Test] + public async Task CanQueryDateTimeByHourAsync() + { + await (AssertQueryAsync(o => o.DateTimeValue.Hour == 4)); } [Test] - public async Task CanQueryByDateTimeAsync() + public async Task CanQueryDateTimeBySecondWhenValueContainsFractionalSecondsAsync() + { + AssertDateTimeWithScaleSupported(); + await (AssertQueryAsync(o => o.DateTimeValueWithScale.Second == 4)); + } + + [Test] + public async Task CanQueryDateTimeOffsetBySecondAsync() { - var x = await ((from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26) - select o).ToListAsync()); + AssertDateTimeOffsetSupported(); + await (AssertQueryAsync(o => o.DateTimeOffsetValue.Second == 4)); + } - Assert.AreEqual(5, x.Count()); + [Test] + public async Task CanQueryDateTimeOffsetByMinuteAsync() + { + AssertDateTimeOffsetSupported(); + await (AssertQueryAsync(o => o.DateTimeOffsetValue.Minute == 4)); } [Test] - public async Task CanQueryByDateTime2Async() + public async Task CanQueryDateTimeOffsetByHourAsync() { - var x = await ((from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26, 0, 1, 0) - select o).ToListAsync()); + AssertDateTimeOffsetSupported(); + await (AssertQueryAsync(o => o.DateTimeOffsetValue.Hour == 4)); + } - Assert.AreEqual(1, x.Count()); + [Test] + public async Task CanQueryDateTimeOffsetBySecondWhenValueContainsFractionalSecondsAsync() + { + AssertDateTimeOffsetSupported(); + await (AssertQueryAsync(o => o.DateTimeOffsetValueWithScale.Second == 4)); } [Test] - public async Task CanSelectYearAsync() + public async Task CanQueryByDateAsync() { - var x = await ((from o in db.Orders - where o.OrderDate.Value.Year == 1998 - select o.OrderDate.Value.Year).ToListAsync()); + await (AssertQueryAsync(o => o.DateTimeValue.Date == new DateTime(1998, 02, 26))); + } - Assert.That(x, Has.All.EqualTo(1998)); - Assert.AreEqual(270, x.Count()); + [Test] + public async Task CanQueryByDateTimeAsync() + { + await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26))); } [Test] - public async Task CanSelectDateAsync() + public async Task CanQueryByDateTime2Async() { - var x = await ((from o in db.Orders - where o.OrderDate.Value.Date == new DateTime(1998, 02, 26) - select o.OrderDate.Value.Date).ToListAsync()); + await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26, 1, 1, 1))); + } - Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26))); - Assert.AreEqual(6, x.Count()); + [Test] + public async Task CanSelectYearAsync() + { + await (AssertQueryAsync(o => o.DateTimeValue.Year == 1998, o => o.DateTimeValue.Year)); } [Test] - public async Task CanSelectDateTimeAsync() + public async Task CanSelectDateAsync() { - var x = await ((from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26) - select o.OrderDate.Value).ToListAsync()); + await (AssertQueryAsync(o => o.DateTimeValue.Date == new DateTime(1998, 02, 26), o => o.DateTimeValue.Date)); + } - Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26))); - Assert.AreEqual(5, x.Count()); + [Test] + public async Task CanSelectDateTimeAsync() + { + await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26), o => o.DateTimeValue)); } [Test] public async Task CanSelectDateTime2Async() { - var x = await ((from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26, 0, 1, 0) - select o.OrderDate.Value).ToListAsync()); + await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26, 1, 1, 1), o => o.DateTimeValue)); + } + + [Test] + public async Task CanSelectDateTimeWithScaleAsync() + { + AssertDateTimeWithScaleSupported(); + await (AssertQueryAsync(o => o.DateTimeValueWithScale == _referenceEntities[0].DateTimeValueWithScale, o => o.DateTimeValueWithScale)); + } + + public class DateTimeTestsClass : IEquatable + { + public int Id { get; set; } + public DateTime DateTimeValue { get; set; } + public DateTime DateTimeValueWithScale { get; set; } + public DateTimeOffset DateTimeOffsetValue { get; set; } + public DateTimeOffset DateTimeOffsetValueWithScale { get; set; } + public DateTime DateValue { get; set; } + + public override bool Equals(object obj) + { + return Equals(obj as DateTimeTestsClass); + } + + public bool Equals(DateTimeTestsClass other) + { + return other is not null && + Id.Equals(other.Id); + } - Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26, 0, 1, 0))); - Assert.AreEqual(1, x.Count()); + public override int GetHashCode() + { + return HashCode.Combine(Id); + } } } } diff --git a/src/NHibernate.Test/Linq/DateTimeTests.cs b/src/NHibernate.Test/Linq/DateTimeTests.cs index 20ec87ff802..73756dbabd1 100644 --- a/src/NHibernate.Test/Linq/DateTimeTests.cs +++ b/src/NHibernate.Test/Linq/DateTimeTests.cs @@ -1,94 +1,248 @@ -using System; +using System; +using System.Data; using System.Linq; +using NHibernate.Cfg; +using NHibernate.SqlTypes; +using NHibernate.Mapping.ByCode; using NUnit.Framework; +using NHibernate.Type; +using NHibernate.Linq; +using System.Linq.Expressions; namespace NHibernate.Test.Linq { [TestFixture] - public class DateTimeTests : LinqTestCase + public class DateTimeTests : TestCase { + private bool DialectSupportsDateTimeOffset => TestDialect.SupportsSqlType(new SqlType(DbType.DateTimeOffset)); + private bool DialectSupportsDateTimeWithScale => TestDialect.SupportsSqlType(new SqlType(DbType.DateTime, 2)); + + private DateTimeTestsClass[] _referenceEntities = new DateTimeTestsClass[] + { + new() {Id =1, DateTimeValue = new DateTime(1998, 02, 26)}, + new() {Id =2, DateTimeValue = new DateTime(1998, 02, 26)}, + new() {Id =3, DateTimeValue = new DateTime(1998, 02, 26, 01, 01, 01)}, + new() {Id =4, DateTimeValue = new DateTime(1998, 02, 26, 02, 02, 02)}, + new() {Id =5, DateTimeValue = new DateTime(1998, 02, 26, 03, 03, 03)}, + new() {Id =6, DateTimeValue = new DateTime(1998, 02, 26, 04, 04, 04)}, + new() {Id =7, DateTimeValue = new DateTime(1998, 03, 01)}, + new() {Id =8, DateTimeValue = new DateTime(2000, 01, 01)} + }; + + protected override string[] Mappings => default; + protected override void AddMappings(Configuration configuration) + { + var modelMapper = new ModelMapper(); + + modelMapper.Class(m => + { + m.Table("datetimetests"); + m.Lazy(false); + m.Id(p => p.Id, p => p.Generator(Generators.Assigned)); + m.Property(p => p.DateValue, c => c.Type()); + m.Property(p => p.DateTimeValue); + m.Property(p => p.DateTimeValueWithScale, c => c.Scale(2)); + if (DialectSupportsDateTimeOffset) + { + m.Property(p => p.DateTimeOffsetValue); + m.Property(p => p.DateTimeOffsetValueWithScale, c => c.Scale(2)); + } + }); + var mapping = modelMapper.CompileMappingForAllExplicitlyAddedEntities(); + configuration.AddMapping(mapping); + } + + protected override void OnSetUp() + { + foreach (var entity in _referenceEntities) + { + entity.DateValue = entity.DateTimeValue.Date; + entity.DateTimeValueWithScale = entity.DateTimeValue.AddSeconds(0.9); + entity.DateTimeOffsetValue = new DateTimeOffset(entity.DateTimeValue, TimeSpan.FromHours(3)); + entity.DateTimeOffsetValueWithScale = new DateTimeOffset(entity.DateTimeValue, TimeSpan.FromHours(3)); + } + + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + foreach (var entity in _referenceEntities) + { + session.Save(entity); + } + trans.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + session.Query().Delete(); + trans.Commit(); + } + } + + private void AssertDateTimeOffsetSupported() + { + if (!DialectSupportsDateTimeOffset) + { + Assert.Ignore("Dialect doesn't support DateTimeOffset"); + } + } + + private void AssertDateTimeWithScaleSupported() + { + if (!DialectSupportsDateTimeWithScale) + { + Assert.Ignore("Dialect doesn't support DateTime with scale (2)"); + } + } + + private void AssertQuery(Expression> where) => AssertQuery(where, x => x.Id); + + private void AssertQuery(Expression> where, Expression> select) + { + using var session = OpenSession(); + var fromDb = session.Query().Where(where).Select(select).ToList(); + var fromMemory = _referenceEntities.AsQueryable().Where(where).Select(select).AsEnumerable().ToList(); //AsEnumerable added to avoid async generator + Assert.That(fromMemory, Has.Count.GreaterThan(0), "Inconclusive, since the query doesn't match anything in the defined set"); + Assert.That(fromDb, Has.Count.EqualTo(fromMemory.Count)); + Assert.That(fromDb, Is.EquivalentTo(fromMemory)); + } + [Test] public void CanQueryByYear() { - var x = (from o in db.Orders - where o.OrderDate.Value.Year == 1998 - select o).ToList(); + AssertQuery(o => o.DateTimeValue.Year == 1998); + } - Assert.AreEqual(270, x.Count()); + [Test] + public void CanQueryDateTimeBySecond() + { + AssertQuery(o => o.DateTimeValue.Second == 4); } [Test] - public void CanQueryByDate() + public void CanQueryDateTimeByMinute() { - var x = (from o in db.Orders - where o.OrderDate.Value.Date == new DateTime(1998, 02, 26) - select o).ToList(); + AssertQuery(o => o.DateTimeValue.Minute == 4); + } - Assert.AreEqual(6, x.Count()); + [Test] + public void CanQueryDateTimeByHour() + { + AssertQuery(o => o.DateTimeValue.Hour == 4); } [Test] - public void CanQueryByDateTime() + public void CanQueryDateTimeBySecondWhenValueContainsFractionalSeconds() + { + AssertDateTimeWithScaleSupported(); + AssertQuery(o => o.DateTimeValueWithScale.Second == 4); + } + + [Test] + public void CanQueryDateTimeOffsetBySecond() { - var x = (from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26) - select o).ToList(); + AssertDateTimeOffsetSupported(); + AssertQuery(o => o.DateTimeOffsetValue.Second == 4); + } - Assert.AreEqual(5, x.Count()); + [Test] + public void CanQueryDateTimeOffsetByMinute() + { + AssertDateTimeOffsetSupported(); + AssertQuery(o => o.DateTimeOffsetValue.Minute == 4); } [Test] - public void CanQueryByDateTime2() + public void CanQueryDateTimeOffsetByHour() { - var x = (from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26, 0, 1, 0) - select o).ToList(); + AssertDateTimeOffsetSupported(); + AssertQuery(o => o.DateTimeOffsetValue.Hour == 4); + } - Assert.AreEqual(1, x.Count()); + [Test] + public void CanQueryDateTimeOffsetBySecondWhenValueContainsFractionalSeconds() + { + AssertDateTimeOffsetSupported(); + AssertQuery(o => o.DateTimeOffsetValueWithScale.Second == 4); } [Test] - public void CanSelectYear() + public void CanQueryByDate() { - var x = (from o in db.Orders - where o.OrderDate.Value.Year == 1998 - select o.OrderDate.Value.Year).ToList(); + AssertQuery(o => o.DateTimeValue.Date == new DateTime(1998, 02, 26)); + } - Assert.That(x, Has.All.EqualTo(1998)); - Assert.AreEqual(270, x.Count()); + [Test] + public void CanQueryByDateTime() + { + AssertQuery(o => o.DateTimeValue == new DateTime(1998, 02, 26)); } [Test] - public void CanSelectDate() + public void CanQueryByDateTime2() { - var x = (from o in db.Orders - where o.OrderDate.Value.Date == new DateTime(1998, 02, 26) - select o.OrderDate.Value.Date).ToList(); + AssertQuery(o => o.DateTimeValue == new DateTime(1998, 02, 26, 1, 1, 1)); + } - Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26))); - Assert.AreEqual(6, x.Count()); + [Test] + public void CanSelectYear() + { + AssertQuery(o => o.DateTimeValue.Year == 1998, o => o.DateTimeValue.Year); } [Test] - public void CanSelectDateTime() + public void CanSelectDate() { - var x = (from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26) - select o.OrderDate.Value).ToList(); + AssertQuery(o => o.DateTimeValue.Date == new DateTime(1998, 02, 26), o => o.DateTimeValue.Date); + } - Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26))); - Assert.AreEqual(5, x.Count()); + [Test] + public void CanSelectDateTime() + { + AssertQuery(o => o.DateTimeValue == new DateTime(1998, 02, 26), o => o.DateTimeValue); } [Test] public void CanSelectDateTime2() { - var x = (from o in db.Orders - where o.OrderDate.Value == new DateTime(1998, 02, 26, 0, 1, 0) - select o.OrderDate.Value).ToList(); + AssertQuery(o => o.DateTimeValue == new DateTime(1998, 02, 26, 1, 1, 1), o => o.DateTimeValue); + } + + [Test] + public void CanSelectDateTimeWithScale() + { + AssertDateTimeWithScaleSupported(); + AssertQuery(o => o.DateTimeValueWithScale == _referenceEntities[0].DateTimeValueWithScale, o => o.DateTimeValueWithScale); + } - Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26, 0, 1, 0))); - Assert.AreEqual(1, x.Count()); + public class DateTimeTestsClass : IEquatable + { + public int Id { get; set; } + public DateTime DateTimeValue { get; set; } + public DateTime DateTimeValueWithScale { get; set; } + public DateTimeOffset DateTimeOffsetValue { get; set; } + public DateTimeOffset DateTimeOffsetValueWithScale { get; set; } + public DateTime DateValue { get; set; } + + public override bool Equals(object obj) + { + return Equals(obj as DateTimeTestsClass); + } + + public bool Equals(DateTimeTestsClass other) + { + return other is not null && + Id.Equals(other.Id); + } + + public override int GetHashCode() + { + return HashCode.Combine(Id); + } } } } diff --git a/src/NHibernate/Dialect/FirebirdDialect.cs b/src/NHibernate/Dialect/FirebirdDialect.cs index 7109480335c..7d688ca5fe3 100644 --- a/src/NHibernate/Dialect/FirebirdDialect.cs +++ b/src/NHibernate/Dialect/FirebirdDialect.cs @@ -537,6 +537,7 @@ private void RegisterDateTimeFunctions() RegisterFunction("addweek", new StandardSQLFunction("addweek", NHibernateUtil.DateTime)); RegisterFunction("addyear", new StandardSQLFunction("addyear", NHibernateUtil.DateTime)); RegisterFunction("getexacttimestamp", new NoArgSQLFunction("getexacttimestamp", NHibernateUtil.DateTime)); + RegisterFunction("secondtruncated", new SQLFunctionTemplate(NHibernateUtil.Int32, "cast(floor(extract(second from ?1)) as int)")); } private void RegisterStringAndCharFunctions() diff --git a/src/NHibernate/Dialect/Oracle8iDialect.cs b/src/NHibernate/Dialect/Oracle8iDialect.cs index 2843db5e58f..e275b0496e2 100644 --- a/src/NHibernate/Dialect/Oracle8iDialect.cs +++ b/src/NHibernate/Dialect/Oracle8iDialect.cs @@ -264,9 +264,11 @@ protected virtual void RegisterFunctions() // Cast is needed because EXTRACT treats DATE not as legacy Oracle DATE but as ANSI DATE, without time elements. // Therefore, you can extract only YEAR, MONTH, and DAY from a DATE value. + // Oracle returns the seconds with fractional precision. It has to be truncated to return the actual second part RegisterFunction("second", new SQLFunctionTemplate(NHibernateUtil.Int32, "extract(second from cast(?1 as timestamp))")); RegisterFunction("minute", new SQLFunctionTemplate(NHibernateUtil.Int32, "extract(minute from cast(?1 as timestamp))")); RegisterFunction("hour", new SQLFunctionTemplate(NHibernateUtil.Int32, "extract(hour from cast(?1 as timestamp))")); + RegisterFunction("secondtruncated", new SQLFunctionTemplate(NHibernateUtil.Int32, "cast(floor(extract(second from cast(?1 as timestamp))) as int)")); RegisterFunction("date", new StandardSQLFunction("trunc", NHibernateUtil.Date)); diff --git a/src/NHibernate/Dialect/PostgreSQLDialect.cs b/src/NHibernate/Dialect/PostgreSQLDialect.cs index 725d530a22a..3053ce17267 100644 --- a/src/NHibernate/Dialect/PostgreSQLDialect.cs +++ b/src/NHibernate/Dialect/PostgreSQLDialect.cs @@ -105,6 +105,8 @@ public PostgreSQLDialect() // and NHibernate.TestDatabaseSetup does install it. RegisterFunction("new_uuid", new NoArgSQLFunction("uuid_generate_v4", NHibernateUtil.Guid)); + RegisterFunction("secondtruncated", new SQLFunctionTemplate(NHibernateUtil.Int32, "cast(floor(extract(second from ?1)) as int)")); + RegisterKeywords(); } diff --git a/src/NHibernate/Linq/Functions/DateTimePropertiesHqlGenerator.cs b/src/NHibernate/Linq/Functions/DateTimePropertiesHqlGenerator.cs index a8b26787f47..fb54256da07 100644 --- a/src/NHibernate/Linq/Functions/DateTimePropertiesHqlGenerator.cs +++ b/src/NHibernate/Linq/Functions/DateTimePropertiesHqlGenerator.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Linq.Expressions; using System.Reflection; +using NHibernate.Engine; using NHibernate.Hql.Ast; using NHibernate.Linq.Visitors; using NHibernate.Util; @@ -33,8 +34,13 @@ public DateTimePropertiesHqlGenerator() public override HqlTreeNode BuildHql(MemberInfo member, Expression expression, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { - return treeBuilder.MethodCall(member.Name.ToLowerInvariant(), + var functionName = member.Name.ToLowerInvariant(); + if (functionName == "second" && (visitor.SessionFactory as ISessionFactoryImplementor)?.Dialect.Functions.ContainsKey("secondtruncated") == true) + { + functionName = "secondtruncated"; + } + return treeBuilder.MethodCall(functionName, visitor.Visit(expression).AsExpression()); } } -} \ No newline at end of file +}