Skip to content

Commit 310c756

Browse files
fixup! Support evaluation of DateTime.Now on db side
Add an option for failing or falling back in case of lack of dialect support
1 parent b57c46f commit 310c756

File tree

8 files changed

+283
-255
lines changed

8 files changed

+283
-255
lines changed

doc/reference/modules/configuration.xml

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -735,14 +735,39 @@ var session = sessions.OpenSession(conn);
735735
<para>
736736
The new pre-evaluation allows them to be converted to HQL function calls which will be run on the db
737737
side. This allows for example to retrieve the server time instead of the client time, or to generate
738-
UUIDs for each row instead of an unique one for all rows. (This does not happen if the dialect does
739-
not support the required HQL function.)
738+
UUIDs for each row instead of an unique one for all rows.
740739
</para>
741740
<para>
742741
The new pre-evaluation will likely be enabled by default in the next major version (6.0).
743742
</para>
744743
</entry>
745744
</row>
745+
<row>
746+
<entry>
747+
<literal>linqtohql.fallback_on_preevaluation</literal>
748+
</entry>
749+
<entry>
750+
When the new pre-evaluation is enabled, should methods which translation is not supported by the current
751+
dialect fallback to pre-evaluation? Defaults to <literal>false</literal>.
752+
<para>
753+
<emphasis role="strong">eg.</emphasis>
754+
<literal>true</literal> | <literal>false</literal>
755+
</para>
756+
<para>
757+
When this fallback option is enabled while legacy pre-evaluation is disabled, properties or functions
758+
like <literal>DateTime.Now</literal> or <literal>Guid.NewGuid()</literal> used in Linq expressions
759+
will not fail when the dialect does not support them, but will instead be pre-evaluated.
760+
</para>
761+
<para>
762+
When this fallback option is disabled while legacy pre-evaluation is disabled, properties or functions
763+
like <literal>DateTime.Now</literal> or <literal>Guid.NewGuid()</literal> used in Linq expressions
764+
will fail when the dialect does not support them.
765+
</para>
766+
<para>
767+
This option has no effect if the legacy pre-evaluation is enabled.
768+
</para>
769+
</entry>
770+
</row>
746771
<row>
747772
<entry>
748773
<literal>sql_exception_converter</literal>

src/NHibernate.Test/Async/Linq/PreEvaluationTests.cs

Lines changed: 33 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -20,166 +20,27 @@
2020
namespace NHibernate.Test.Linq
2121
{
2222
using System.Threading.Tasks;
23-
[TestFixture]
23+
[TestFixture(false, false)]
24+
[TestFixture(true, false)]
25+
[TestFixture(false, true)]
2426
public class PreEvaluationTestsAsync : LinqTestCase
2527
{
26-
protected virtual bool LegacyPreEvaluation => false;
28+
private readonly bool LegacyPreEvaluation;
29+
private readonly bool FallbackOnPreEvaluation;
30+
31+
public PreEvaluationTestsAsync(bool legacy, bool fallback)
32+
{
33+
LegacyPreEvaluation = legacy;
34+
FallbackOnPreEvaluation = fallback;
35+
}
2736

2837
protected override void Configure(Configuration configuration)
2938
{
3039
base.Configure(configuration);
3140

3241
configuration.SetProperty(Environment.FormatSql, "false");
3342
configuration.SetProperty(Environment.LinqToHqlLegacyPreEvaluation, LegacyPreEvaluation.ToString());
34-
}
35-
36-
[Test]
37-
public async Task CanQueryByDateTimeNowAsync()
38-
{
39-
using (var spy = new SqlLogSpy())
40-
{
41-
var x = await (db.Orders.CountAsync(o => o.OrderDate.Value < DateTime.Now));
42-
43-
Assert.That(x, Is.GreaterThan(0));
44-
AssertFunctionInSql("current_timestamp", spy);
45-
}
46-
}
47-
48-
[Test]
49-
public async Task CanSelectDateTimeNowAsync()
50-
{
51-
using (var spy = new SqlLogSpy())
52-
{
53-
var x =
54-
await (db
55-
.Orders.Select(o => new { id = o.OrderId, d = DateTime.Now })
56-
.OrderBy(o => o.id).Take(1).ToListAsync());
57-
58-
Assert.That(x, Has.Count.GreaterThan(0));
59-
AssertFunctionInSql("current_timestamp", spy);
60-
}
61-
}
62-
63-
[Test]
64-
public async Task CanQueryByDateTimeUtcNowAsync()
65-
{
66-
using (var spy = new SqlLogSpy())
67-
{
68-
var x = await (db.Orders.CountAsync(o => o.OrderDate.Value < DateTime.UtcNow));
69-
70-
Assert.That(x, Is.GreaterThan(0));
71-
AssertFunctionInSql("current_utctimestamp", spy);
72-
}
73-
}
74-
75-
[Test]
76-
public async Task CanSelectDateTimeUtcNowAsync()
77-
{
78-
using (var spy = new SqlLogSpy())
79-
{
80-
var x =
81-
await (db
82-
.Orders.Select(o => new { id = o.OrderId, d = DateTime.UtcNow })
83-
.OrderBy(o => o.id).Take(1).ToListAsync());
84-
85-
Assert.That(x, Has.Count.GreaterThan(0));
86-
AssertFunctionInSql("current_utctimestamp", spy);
87-
}
88-
}
89-
90-
[Test]
91-
public async Task CanQueryByDateTimeTodayAsync()
92-
{
93-
using (var spy = new SqlLogSpy())
94-
{
95-
var x = await (db.Orders.CountAsync(o => o.OrderDate.Value < DateTime.Today));
96-
97-
Assert.That(x, Is.GreaterThan(0));
98-
AssertFunctionInSql("current_date", spy);
99-
}
100-
}
101-
102-
[Test]
103-
public async Task CanSelectDateTimeTodayAsync()
104-
{
105-
using (var spy = new SqlLogSpy())
106-
{
107-
var x =
108-
await (db
109-
.Orders.Select(o => new { id = o.OrderId, d = DateTime.Today })
110-
.OrderBy(o => o.id).Take(1).ToListAsync());
111-
112-
Assert.That(x, Has.Count.GreaterThan(0));
113-
AssertFunctionInSql("current_date", spy);
114-
}
115-
}
116-
117-
[Test]
118-
public async Task CanQueryByDateTimeOffsetTimeNowAsync()
119-
{
120-
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
121-
Assert.Ignore("Dialect does not support DateTimeOffSet");
122-
123-
using (var spy = new SqlLogSpy())
124-
{
125-
var testDate = DateTimeOffset.Now.AddDays(-1);
126-
var x = await (db.Orders.CountAsync(o => testDate < DateTimeOffset.Now));
127-
128-
Assert.That(x, Is.GreaterThan(0));
129-
AssertFunctionInSql("current_timestamp_offset", spy);
130-
}
131-
}
132-
133-
[Test]
134-
public async Task CanSelectDateTimeOffsetNowAsync()
135-
{
136-
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
137-
Assert.Ignore("Dialect does not support DateTimeOffSet");
138-
139-
using (var spy = new SqlLogSpy())
140-
{
141-
var x =
142-
await (db
143-
.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.Now })
144-
.OrderBy(o => o.id).Take(1).ToListAsync());
145-
146-
Assert.That(x, Has.Count.GreaterThan(0));
147-
AssertFunctionInSql("current_timestamp_offset", spy);
148-
}
149-
}
150-
151-
[Test]
152-
public async Task CanQueryByDateTimeOffsetUtcNowAsync()
153-
{
154-
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
155-
Assert.Ignore("Dialect does not support DateTimeOffSet");
156-
157-
using (var spy = new SqlLogSpy())
158-
{
159-
var testDate = DateTimeOffset.UtcNow.AddDays(-1);
160-
var x = await (db.Orders.CountAsync(o => testDate < DateTimeOffset.UtcNow));
161-
162-
Assert.That(x, Is.GreaterThan(0));
163-
AssertFunctionInSql("current_utctimestamp_offset", spy);
164-
}
165-
}
166-
167-
[Test]
168-
public async Task CanSelectDateTimeOffsetUtcNowAsync()
169-
{
170-
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
171-
Assert.Ignore("Dialect does not support DateTimeOffSet");
172-
173-
using (var spy = new SqlLogSpy())
174-
{
175-
var x =
176-
await (db
177-
.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.UtcNow })
178-
.OrderBy(o => o.id).Take(1).ToListAsync());
179-
180-
Assert.That(x, Has.Count.GreaterThan(0));
181-
AssertFunctionInSql("current_utctimestamp_offset", spy);
182-
}
43+
configuration.SetProperty(Environment.LinqToHqlFallbackOnPreEvaluation, FallbackOnPreEvaluation.ToString());
18344
}
18445

18546
[Test]
@@ -407,6 +268,27 @@ public async Task CanSelectRandomIntWithMinMaxAsync()
407268
}
408269
}
409270

271+
private void RunTest(bool isSupported, Action<SqlLogSpy> test)
272+
{
273+
using (var spy = new SqlLogSpy())
274+
{
275+
try
276+
{
277+
test(spy);
278+
}
279+
catch (QueryException)
280+
{
281+
if (!isSupported && !FallbackOnPreEvaluation)
282+
// Expected failure
283+
return;
284+
throw;
285+
}
286+
}
287+
288+
if (!isSupported && !FallbackOnPreEvaluation)
289+
Assert.Fail("The test should have thrown a QueryException, but has not thrown anything");
290+
}
291+
410292
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
411293
{
412294
if (!IsFunctionSupported(functionName))
@@ -420,10 +302,4 @@ private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
420302
Assert.That(spy.GetWholeLog(), Does.Contain(function));
421303
}
422304
}
423-
424-
[TestFixture]
425-
public class PreEvaluationLegacyTestsAsync : PreEvaluationTestsAsync
426-
{
427-
protected override bool LegacyPreEvaluation => true;
428-
}
429305
}

0 commit comments

Comments
 (0)