From 8ebeb931204f7777a4e4f953cd4caa89fc2dff5e Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 3 May 2023 14:43:18 +1000 Subject: [PATCH] Fix support for null datetime parameters for Npgsql 6+ --- .../Async/NHSpecificTest/GH3291/Fixture.cs | 79 +++++++++++++++++++ .../NHSpecificTest/GH3291/Fixture.cs | 67 ++++++++++++++++ .../NHSpecificTest/GH3291/Mappings.hbm.xml | 11 +++ .../NHSpecificTest/GH3291/Person.cs | 11 +++ .../TestDatabaseSetup.cs | 6 +- src/NHibernate/Driver/NpgsqlDriver.cs | 21 ++++- 6 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/GH3291/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3291/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3291/Mappings.hbm.xml create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3291/Person.cs diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3291/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3291/Fixture.cs new file mode 100644 index 00000000000..0d898115b80 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3291/Fixture.cs @@ -0,0 +1,79 @@ +//------------------------------------------------------------------------------ +// +// 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.Criterion; +using NUnit.Framework; +using NHibernate.Linq; + +namespace NHibernate.Test.NHSpecificTest.GH3291 +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override void OnSetUp() + { + using var session = OpenSession(); + using var transaction = session.BeginTransaction(); + + var e1 = new Person { Name = "Bob", DateOfBirth = new DateTime(2009, 12, 23) }; + session.Save(e1); + + var e2 = new Person { Name = "Sally", DateOfBirth = new DateTime(2018, 9, 30) }; + session.Save(e2); + + transaction.Commit(); + } + + protected override void OnTearDown() + { + using var session = OpenSession(); + using var transaction = session.BeginTransaction(); + + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + + [Test] + public async Task LinqAsync() + { + using var session = OpenSession(); + using var _ = session.BeginTransaction(); + + DateTime? dateOfSearch = null; + + var result = await (( + from person in session.Query() + where dateOfSearch == null || person.DateOfBirth > dateOfSearch + select person).ToListAsync()); + + Assert.That(result, Has.Count.EqualTo(2)); + } + + [Test] + public async Task HqlAsync() + { + using var session = OpenSession(); + using var _ = session.BeginTransaction(); + + DateTime? dateOfSearch = null; + + var result = + await (session.CreateQuery("from Person where :DateOfSearch is null OR DateOfBirth > :DateOfSearch") + .SetParameter("DateOfSearch", dateOfSearch, NHibernateUtil.DateTime) + .ListAsync()); + + Assert.That(result, Has.Count.EqualTo(2)); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3291/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3291/Fixture.cs new file mode 100644 index 00000000000..fa01d89b8f9 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3291/Fixture.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using NHibernate.Criterion; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3291 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnSetUp() + { + using var session = OpenSession(); + using var transaction = session.BeginTransaction(); + + var e1 = new Person { Name = "Bob", DateOfBirth = new DateTime(2009, 12, 23) }; + session.Save(e1); + + var e2 = new Person { Name = "Sally", DateOfBirth = new DateTime(2018, 9, 30) }; + session.Save(e2); + + transaction.Commit(); + } + + protected override void OnTearDown() + { + using var session = OpenSession(); + using var transaction = session.BeginTransaction(); + + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + + [Test] + public void Linq() + { + using var session = OpenSession(); + using var _ = session.BeginTransaction(); + + DateTime? dateOfSearch = null; + + var result = ( + from person in session.Query() + where dateOfSearch == null || person.DateOfBirth > dateOfSearch + select person).ToList(); + + Assert.That(result, Has.Count.EqualTo(2)); + } + + [Test] + public void Hql() + { + using var session = OpenSession(); + using var _ = session.BeginTransaction(); + + DateTime? dateOfSearch = null; + + var result = + session.CreateQuery("from Person where :DateOfSearch is null OR DateOfBirth > :DateOfSearch") + .SetParameter("DateOfSearch", dateOfSearch, NHibernateUtil.DateTime) + .List(); + + Assert.That(result, Has.Count.EqualTo(2)); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3291/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH3291/Mappings.hbm.xml new file mode 100644 index 00000000000..1088c98b593 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3291/Mappings.hbm.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/NHibernate.Test/NHSpecificTest/GH3291/Person.cs b/src/NHibernate.Test/NHSpecificTest/GH3291/Person.cs new file mode 100644 index 00000000000..d80efc2e094 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3291/Person.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH3291 +{ + class Person + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + public virtual DateTime? DateOfBirth { get; set; } + } +} diff --git a/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs b/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs index 5dd5fc8fe22..deea09fb3a5 100644 --- a/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs +++ b/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs @@ -182,11 +182,7 @@ private static void SetupNpgsql(Cfg.Configuration cfg) using (var cmd = conn.CreateCommand()) { - cmd.CommandText = - @"CREATE OR REPLACE FUNCTION uuid_generate_v4() - RETURNS uuid - AS '$libdir/uuid-ossp', 'uuid_generate_v4' - VOLATILE STRICT LANGUAGE C;"; + cmd.CommandText = "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";"; cmd.ExecuteNonQuery(); } diff --git a/src/NHibernate/Driver/NpgsqlDriver.cs b/src/NHibernate/Driver/NpgsqlDriver.cs index c638e256221..69c39821e1c 100644 --- a/src/NHibernate/Driver/NpgsqlDriver.cs +++ b/src/NHibernate/Driver/NpgsqlDriver.cs @@ -1,3 +1,4 @@ +using System; using System.Data; using System.Data.Common; using NHibernate.AdoNet; @@ -74,14 +75,28 @@ protected override void InitializeParameter(DbParameter dbParam, string name, Sq // Since the .NET currency type has 4 decimal places, we use a decimal type in PostgreSQL instead of its native 2 decimal currency type. dbParam.DbType = DbType.Decimal; } - else if (DriverVersionMajor < 6 || sqlType.DbType != DbType.DateTime) + else { dbParam.DbType = sqlType.DbType; } - else + } + + public override void AdjustCommand(DbCommand command) + { + if (DriverVersionMajor >= 6) { - // Let Npgsql 6 driver to decide parameter type + for (var i = 0; i < command.Parameters.Count; i++) + { + var parameter = command.Parameters[i]; + if (parameter.Value is DateTime) + { + // Let Npgsql 6 driver to decide parameter type + parameter.ResetDbType(); + } + } } + + base.AdjustCommand(command); } // Prior to v3, Npgsql was expecting DateTime for time.