Skip to content

Commit 169b0d4

Browse files
Backport handling of null DateTime parameters in Npgsql 6+ (#3300)
Fixes #3291 in 5.3.x Co-authored-by: Alex Zaytsev <hazzik@gmail.com>
1 parent 342c01f commit 169b0d4

File tree

6 files changed

+202
-8
lines changed

6 files changed

+202
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System;
12+
using System.Linq;
13+
using NUnit.Framework;
14+
using NHibernate.Linq;
15+
16+
namespace NHibernate.Test.NHSpecificTest.GH3291
17+
{
18+
using System.Threading.Tasks;
19+
[TestFixture]
20+
public class FixtureAsync : BugTestCase
21+
{
22+
protected override void OnSetUp()
23+
{
24+
using (var session = OpenSession())
25+
using (var transaction = session.BeginTransaction())
26+
{
27+
var e1 = new Person { Name = "Bob", DateOfBirth = new DateTime(2009, 12, 23) };
28+
session.Save(e1);
29+
30+
var e2 = new Person { Name = "Sally", DateOfBirth = new DateTime(2018, 9, 30) };
31+
session.Save(e2);
32+
33+
transaction.Commit();
34+
}
35+
}
36+
37+
protected override void OnTearDown()
38+
{
39+
using (var session = OpenSession())
40+
using (var transaction = session.BeginTransaction())
41+
{
42+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
43+
44+
transaction.Commit();
45+
}
46+
}
47+
48+
[Test]
49+
public async Task LinqAsync()
50+
{
51+
using (var session = OpenSession())
52+
using (session.BeginTransaction())
53+
{
54+
DateTime? dateOfSearch = null;
55+
56+
var result = await ((
57+
from person in session.Query<Person>()
58+
where dateOfSearch == null || person.DateOfBirth > dateOfSearch
59+
select person).ToListAsync());
60+
61+
Assert.That(result, Has.Count.EqualTo(2));
62+
}
63+
}
64+
65+
[Test]
66+
public async Task HqlAsync()
67+
{
68+
using (var session = OpenSession())
69+
using (session.BeginTransaction())
70+
{
71+
DateTime? dateOfSearch = null;
72+
73+
var result =
74+
await (session.CreateQuery("from Person where :DateOfSearch is null OR DateOfBirth > :DateOfSearch")
75+
.SetParameter("DateOfSearch", dateOfSearch, NHibernateUtil.DateTime)
76+
.ListAsync<Person>());
77+
78+
Assert.That(result, Has.Count.EqualTo(2));
79+
}
80+
}
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
5+
namespace NHibernate.Test.NHSpecificTest.GH3291
6+
{
7+
[TestFixture]
8+
public class Fixture : BugTestCase
9+
{
10+
protected override void OnSetUp()
11+
{
12+
using (var session = OpenSession())
13+
using (var transaction = session.BeginTransaction())
14+
{
15+
var e1 = new Person { Name = "Bob", DateOfBirth = new DateTime(2009, 12, 23) };
16+
session.Save(e1);
17+
18+
var e2 = new Person { Name = "Sally", DateOfBirth = new DateTime(2018, 9, 30) };
19+
session.Save(e2);
20+
21+
transaction.Commit();
22+
}
23+
}
24+
25+
protected override void OnTearDown()
26+
{
27+
using (var session = OpenSession())
28+
using (var transaction = session.BeginTransaction())
29+
{
30+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
31+
32+
transaction.Commit();
33+
}
34+
}
35+
36+
[Test]
37+
public void Linq()
38+
{
39+
using (var session = OpenSession())
40+
using (session.BeginTransaction())
41+
{
42+
DateTime? dateOfSearch = null;
43+
44+
var result = (
45+
from person in session.Query<Person>()
46+
where dateOfSearch == null || person.DateOfBirth > dateOfSearch
47+
select person).ToList();
48+
49+
Assert.That(result, Has.Count.EqualTo(2));
50+
}
51+
}
52+
53+
[Test]
54+
public void Hql()
55+
{
56+
using (var session = OpenSession())
57+
using (session.BeginTransaction())
58+
{
59+
DateTime? dateOfSearch = null;
60+
61+
var result =
62+
session.CreateQuery("from Person where :DateOfSearch is null OR DateOfBirth > :DateOfSearch")
63+
.SetParameter("DateOfSearch", dateOfSearch, NHibernateUtil.DateTime)
64+
.List<Person>();
65+
66+
Assert.That(result, Has.Count.EqualTo(2));
67+
}
68+
}
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
3+
namespace="NHibernate.Test.NHSpecificTest.GH3291">
4+
5+
<class name="Person">
6+
<id name="Id" generator="guid.comb"/>
7+
<property name="Name"/>
8+
<property name="DateOfBirth" />
9+
</class>
10+
11+
</hibernate-mapping>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH3291
4+
{
5+
class Person
6+
{
7+
public virtual Guid Id { get; set; }
8+
public virtual string Name { get; set; }
9+
public virtual DateTime? DateOfBirth { get; set; }
10+
}
11+
}

src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,7 @@ private static void SetupNpgsql(Cfg.Configuration cfg)
182182

183183
using (var cmd = conn.CreateCommand())
184184
{
185-
cmd.CommandText =
186-
@"CREATE OR REPLACE FUNCTION uuid_generate_v4()
187-
RETURNS uuid
188-
AS '$libdir/uuid-ossp', 'uuid_generate_v4'
189-
VOLATILE STRICT LANGUAGE C;";
185+
cmd.CommandText = "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";";
190186

191187
cmd.ExecuteNonQuery();
192188
}

src/NHibernate/Driver/NpgsqlDriver.cs

+27-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Data;
23
using System.Data.Common;
34
using NHibernate.AdoNet;
@@ -74,14 +75,37 @@ protected override void InitializeParameter(DbParameter dbParam, string name, Sq
7475
// Since the .NET currency type has 4 decimal places, we use a decimal type in PostgreSQL instead of its native 2 decimal currency type.
7576
dbParam.DbType = DbType.Decimal;
7677
}
77-
else if (DriverVersionMajor < 6 || sqlType.DbType != DbType.DateTime)
78+
else
7879
{
7980
dbParam.DbType = sqlType.DbType;
8081
}
81-
else
82+
}
83+
84+
public override void AdjustCommand(DbCommand command)
85+
{
86+
if (DriverVersionMajor >= 6)
8287
{
83-
// Let Npgsql 6 driver to decide parameter type
88+
for (var i = 0; i < command.Parameters.Count; i++)
89+
{
90+
var parameter = command.Parameters[i];
91+
if (parameter.DbType == DbType.DateTime &&
92+
parameter.Value is DateTime dateTime &&
93+
dateTime.Kind != DateTimeKind.Utc)
94+
{
95+
// There are breaking changes in Npgsql 6 as following:
96+
// UTC DateTime is now strictly mapped to timestamptz,
97+
// while Local/Unspecified DateTime is now strictly mapped to timestamp.
98+
//
99+
// DbType.DateTime now maps to timestamptz, not timestamp.
100+
// DbType.DateTime2 continues to map to timestamp
101+
//
102+
// See more details here: https://www.npgsql.org/doc/release-notes/6.0.html#detailed-notes
103+
parameter.DbType = DbType.DateTime2;
104+
}
105+
}
84106
}
107+
108+
base.AdjustCommand(command);
85109
}
86110

87111
// Prior to v3, Npgsql was expecting DateTime for time.

0 commit comments

Comments
 (0)