Skip to content

Commit 2d2c79a

Browse files
Support evaluation of DateTime.Now on db side
And of all similar properties: UtcNow, Today, and DateTimeOffset's ones. Part of #959
1 parent fe440a0 commit 2d2c79a

31 files changed

+746
-28
lines changed

doc/reference/modules/configuration.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,28 @@ var session = sessions.OpenSession(conn);
717717
</para>
718718
</entry>
719719
</row>
720+
<row>
721+
<entry>
722+
<literal>linqtohql.legacy_preevaluation</literal>
723+
</entry>
724+
<entry>
725+
Whether to use the legacy pre-evaluation or not in Linq queries. Defaults to <literal>false</literal>.
726+
<para>
727+
<emphasis role="strong">eg.</emphasis>
728+
<literal>true</literal> | <literal>false</literal>
729+
</para>
730+
<para>
731+
Legacy pre-evaluation is causing special properties or functions like DateTime.Now or Guid.NewGuid()
732+
to be always evaluated with the .Net runtime and replaced in the query by parameter values.
733+
</para>
734+
<para>
735+
The new pre-evaluation allows them to be converted to HQL function calls which will be run on the db
736+
side. This allows by example to retrieve the server time instead of the client time, or to generate
737+
GUIDs for each row instead of an unique one for all rows. (This does not happen if the dialect does
738+
not support the required HQL function.)
739+
</para>
740+
</entry>
741+
</row>
720742
<row>
721743
<entry>
722744
<literal>sql_exception_converter</literal>
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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.Collections.Generic;
13+
using System.Linq;
14+
using NHibernate.Cfg;
15+
using NHibernate.SqlTypes;
16+
using NUnit.Framework;
17+
using Environment = NHibernate.Cfg.Environment;
18+
using NHibernate.Linq;
19+
20+
namespace NHibernate.Test.Linq
21+
{
22+
using System.Threading.Tasks;
23+
[TestFixture]
24+
public class PreEvaluationTestsAsync : LinqTestCase
25+
{
26+
protected virtual bool LegacyPreEvaluation => false;
27+
28+
protected override void Configure(Configuration configuration)
29+
{
30+
base.Configure(configuration);
31+
32+
configuration.SetProperty(Environment.FormatSql, "false");
33+
if (LegacyPreEvaluation)
34+
configuration.SetProperty(Environment.LinqToHqlLegacyPreEvaluation, "true");
35+
}
36+
37+
[Test]
38+
public async Task CanQueryByDateTimeNowAsync()
39+
{
40+
using (var spy = new SqlLogSpy())
41+
{
42+
var x = await (db.Orders.Where(o => o.OrderDate.Value < DateTime.Now).ToListAsync());
43+
44+
Assert.That(x, Has.Count.GreaterThan(0));
45+
AssertFunctionInSql("current_timestamp", spy);
46+
}
47+
}
48+
49+
[Test]
50+
public async Task CanSelectDateTimeNowAsync()
51+
{
52+
using (var spy = new SqlLogSpy())
53+
{
54+
var x = await (db.Orders.Select(o => new { id = o.OrderId, d = DateTime.Now }).ToListAsync());
55+
56+
Assert.That(x, Has.Count.GreaterThan(0));
57+
AssertFunctionInSql("current_timestamp", spy);
58+
}
59+
}
60+
61+
[Test]
62+
public async Task CanQueryByDateTimeUtcNowAsync()
63+
{
64+
using (var spy = new SqlLogSpy())
65+
{
66+
var x = await (db.Orders.Where(o => o.OrderDate.Value < DateTime.UtcNow).ToListAsync());
67+
68+
Assert.That(x, Has.Count.GreaterThan(0));
69+
AssertFunctionInSql("current_utctimestamp", spy);
70+
}
71+
}
72+
73+
[Test]
74+
public async Task CanSelectDateTimeUtcNowAsync()
75+
{
76+
using (var spy = new SqlLogSpy())
77+
{
78+
var x = await (db.Orders.Select(o => new { id = o.OrderId, d = DateTime.UtcNow }).ToListAsync());
79+
80+
Assert.That(x, Has.Count.GreaterThan(0));
81+
AssertFunctionInSql("current_utctimestamp", spy);
82+
}
83+
}
84+
85+
[Test]
86+
public async Task CanQueryByDateTimeTodayAsync()
87+
{
88+
using (var spy = new SqlLogSpy())
89+
{
90+
var x = await (db.Orders.Where(o => o.OrderDate.Value < DateTime.Today).ToListAsync());
91+
92+
Assert.That(x, Has.Count.GreaterThan(0));
93+
AssertFunctionInSql("current_date", spy);
94+
}
95+
}
96+
97+
[Test]
98+
public async Task CanSelectDateTimeTodayAsync()
99+
{
100+
using (var spy = new SqlLogSpy())
101+
{
102+
var x = await (db.Orders.Select(o => new { id = o.OrderId, d = DateTime.Today }).ToListAsync());
103+
104+
Assert.That(x, Has.Count.GreaterThan(0));
105+
AssertFunctionInSql("current_date", spy);
106+
}
107+
}
108+
109+
[Test]
110+
public async Task CanQueryByDateTimeOffsetTimeNowAsync()
111+
{
112+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
113+
Assert.Ignore("Dialect does not support DateTimeOffSet");
114+
115+
using (var spy = new SqlLogSpy())
116+
{
117+
var testDate = DateTimeOffset.Now.AddDays(-1);
118+
var x = await (db.Orders.Where(o => testDate < DateTimeOffset.Now).ToListAsync());
119+
120+
Assert.That(x, Has.Count.GreaterThan(0));
121+
AssertFunctionInSql("current_timestamp_offset", spy);
122+
}
123+
}
124+
125+
[Test]
126+
public async Task CanSelectDateTimeOffsetNowAsync()
127+
{
128+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
129+
Assert.Ignore("Dialect does not support DateTimeOffSet");
130+
131+
using (var spy = new SqlLogSpy())
132+
{
133+
var x = await (db.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.Now }).ToListAsync());
134+
135+
Assert.That(x, Has.Count.GreaterThan(0));
136+
AssertFunctionInSql("current_timestamp_offset", spy);
137+
}
138+
}
139+
140+
[Test]
141+
public async Task CanQueryByDateTimeOffsetUtcNowAsync()
142+
{
143+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
144+
Assert.Ignore("Dialect does not support DateTimeOffSet");
145+
146+
using (var spy = new SqlLogSpy())
147+
{
148+
var testDate = DateTimeOffset.UtcNow.AddDays(-1);
149+
var x = await (db.Orders.Where(o => testDate < DateTimeOffset.UtcNow).ToListAsync());
150+
151+
Assert.That(x, Has.Count.GreaterThan(0));
152+
AssertFunctionInSql("current_utctimestamp_offset", spy);
153+
}
154+
}
155+
156+
[Test]
157+
public async Task CanSelectDateTimeOffsetUtcNowAsync()
158+
{
159+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
160+
Assert.Ignore("Dialect does not support DateTimeOffSet");
161+
162+
using (var spy = new SqlLogSpy())
163+
{
164+
var x = await (db.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.UtcNow }).ToListAsync());
165+
166+
Assert.That(x, Has.Count.GreaterThan(0));
167+
AssertFunctionInSql("current_utctimestamp_offset", spy);
168+
}
169+
}
170+
171+
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
172+
{
173+
if (!IsFunctionSupported(functionName))
174+
Assert.Inconclusive($"{functionName} is not supported by the dialect");
175+
176+
var function = Dialect.Functions[functionName].Render(new List<object>(), Sfi).ToString();
177+
178+
if (LegacyPreEvaluation)
179+
Assert.That(spy.GetWholeLog(), Does.Not.Contain(function));
180+
else
181+
Assert.That(spy.GetWholeLog(), Does.Contain(function));
182+
}
183+
}
184+
185+
[TestFixture]
186+
public class PreEvaluationLegacyTestsAsync : PreEvaluationTestsAsync
187+
{
188+
protected override bool LegacyPreEvaluation => true;
189+
}
190+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using NHibernate.Cfg;
5+
using NHibernate.SqlTypes;
6+
using NUnit.Framework;
7+
using Environment = NHibernate.Cfg.Environment;
8+
9+
namespace NHibernate.Test.Linq
10+
{
11+
[TestFixture]
12+
public class PreEvaluationTests : LinqTestCase
13+
{
14+
protected virtual bool LegacyPreEvaluation => false;
15+
16+
protected override void Configure(Configuration configuration)
17+
{
18+
base.Configure(configuration);
19+
20+
configuration.SetProperty(Environment.FormatSql, "false");
21+
if (LegacyPreEvaluation)
22+
configuration.SetProperty(Environment.LinqToHqlLegacyPreEvaluation, "true");
23+
}
24+
25+
[Test]
26+
public void CanQueryByDateTimeNow()
27+
{
28+
using (var spy = new SqlLogSpy())
29+
{
30+
var x = db.Orders.Where(o => o.OrderDate.Value < DateTime.Now).ToList();
31+
32+
Assert.That(x, Has.Count.GreaterThan(0));
33+
AssertFunctionInSql("current_timestamp", spy);
34+
}
35+
}
36+
37+
[Test]
38+
public void CanSelectDateTimeNow()
39+
{
40+
using (var spy = new SqlLogSpy())
41+
{
42+
var x = db.Orders.Select(o => new { id = o.OrderId, d = DateTime.Now }).ToList();
43+
44+
Assert.That(x, Has.Count.GreaterThan(0));
45+
AssertFunctionInSql("current_timestamp", spy);
46+
}
47+
}
48+
49+
[Test]
50+
public void CanQueryByDateTimeUtcNow()
51+
{
52+
using (var spy = new SqlLogSpy())
53+
{
54+
var x = db.Orders.Where(o => o.OrderDate.Value < DateTime.UtcNow).ToList();
55+
56+
Assert.That(x, Has.Count.GreaterThan(0));
57+
AssertFunctionInSql("current_utctimestamp", spy);
58+
}
59+
}
60+
61+
[Test]
62+
public void CanSelectDateTimeUtcNow()
63+
{
64+
using (var spy = new SqlLogSpy())
65+
{
66+
var x = db.Orders.Select(o => new { id = o.OrderId, d = DateTime.UtcNow }).ToList();
67+
68+
Assert.That(x, Has.Count.GreaterThan(0));
69+
AssertFunctionInSql("current_utctimestamp", spy);
70+
}
71+
}
72+
73+
[Test]
74+
public void CanQueryByDateTimeToday()
75+
{
76+
using (var spy = new SqlLogSpy())
77+
{
78+
var x = db.Orders.Where(o => o.OrderDate.Value < DateTime.Today).ToList();
79+
80+
Assert.That(x, Has.Count.GreaterThan(0));
81+
AssertFunctionInSql("current_date", spy);
82+
}
83+
}
84+
85+
[Test]
86+
public void CanSelectDateTimeToday()
87+
{
88+
using (var spy = new SqlLogSpy())
89+
{
90+
var x = db.Orders.Select(o => new { id = o.OrderId, d = DateTime.Today }).ToList();
91+
92+
Assert.That(x, Has.Count.GreaterThan(0));
93+
AssertFunctionInSql("current_date", spy);
94+
}
95+
}
96+
97+
[Test]
98+
public void CanQueryByDateTimeOffsetTimeNow()
99+
{
100+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
101+
Assert.Ignore("Dialect does not support DateTimeOffSet");
102+
103+
using (var spy = new SqlLogSpy())
104+
{
105+
var testDate = DateTimeOffset.Now.AddDays(-1);
106+
var x = db.Orders.Where(o => testDate < DateTimeOffset.Now).ToList();
107+
108+
Assert.That(x, Has.Count.GreaterThan(0));
109+
AssertFunctionInSql("current_timestamp_offset", spy);
110+
}
111+
}
112+
113+
[Test]
114+
public void CanSelectDateTimeOffsetNow()
115+
{
116+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
117+
Assert.Ignore("Dialect does not support DateTimeOffSet");
118+
119+
using (var spy = new SqlLogSpy())
120+
{
121+
var x = db.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.Now }).ToList();
122+
123+
Assert.That(x, Has.Count.GreaterThan(0));
124+
AssertFunctionInSql("current_timestamp_offset", spy);
125+
}
126+
}
127+
128+
[Test]
129+
public void CanQueryByDateTimeOffsetUtcNow()
130+
{
131+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
132+
Assert.Ignore("Dialect does not support DateTimeOffSet");
133+
134+
using (var spy = new SqlLogSpy())
135+
{
136+
var testDate = DateTimeOffset.UtcNow.AddDays(-1);
137+
var x = db.Orders.Where(o => testDate < DateTimeOffset.UtcNow).ToList();
138+
139+
Assert.That(x, Has.Count.GreaterThan(0));
140+
AssertFunctionInSql("current_utctimestamp_offset", spy);
141+
}
142+
}
143+
144+
[Test]
145+
public void CanSelectDateTimeOffsetUtcNow()
146+
{
147+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
148+
Assert.Ignore("Dialect does not support DateTimeOffSet");
149+
150+
using (var spy = new SqlLogSpy())
151+
{
152+
var x = db.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.UtcNow }).ToList();
153+
154+
Assert.That(x, Has.Count.GreaterThan(0));
155+
AssertFunctionInSql("current_utctimestamp_offset", spy);
156+
}
157+
}
158+
159+
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
160+
{
161+
if (!IsFunctionSupported(functionName))
162+
Assert.Inconclusive($"{functionName} is not supported by the dialect");
163+
164+
var function = Dialect.Functions[functionName].Render(new List<object>(), Sfi).ToString();
165+
166+
if (LegacyPreEvaluation)
167+
Assert.That(spy.GetWholeLog(), Does.Not.Contain(function));
168+
else
169+
Assert.That(spy.GetWholeLog(), Does.Contain(function));
170+
}
171+
}
172+
173+
[TestFixture]
174+
public class PreEvaluationLegacyTests : PreEvaluationTests
175+
{
176+
protected override bool LegacyPreEvaluation => true;
177+
}
178+
}

0 commit comments

Comments
 (0)