Skip to content

Commit a4bf411

Browse files
Support evaluation of Random.Next and NextDouble on db side
Fixes #959, along with two previous commits
1 parent 746acd6 commit a4bf411

19 files changed

+531
-8
lines changed

src/NHibernate.Test/Async/Hql/HQLFunctions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1079,9 +1079,9 @@ public async Task Current_DateAsync()
10791079
public async Task Current_Date_IsLowestTimeOfDayAsync()
10801080
{
10811081
AssumeFunctionSupported("current_date");
1082-
var now = DateTime.Now;
10831082
if (!TestDialect.SupportsNonDataBoundCondition)
10841083
Assert.Ignore("Test is not supported by the target database");
1084+
var now = DateTime.Now;
10851085
if (now.TimeOfDay < TimeSpan.FromMinutes(5) || now.TimeOfDay > TimeSpan.Parse("23:55"))
10861086
Assert.Ignore("Test is unreliable around midnight");
10871087

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

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,179 @@ public async Task CanSelectNewGuidAsync()
217217
}
218218
}
219219

220+
[Test]
221+
public async Task CanQueryByRandomDoubleAsync()
222+
{
223+
using (var spy = new SqlLogSpy())
224+
{
225+
var random = new Random();
226+
var x = await (db.Orders.CountAsync(o => o.OrderId > random.NextDouble()));
227+
228+
Assert.That(x, Is.GreaterThan(0));
229+
AssertFunctionInSql("random", spy);
230+
}
231+
}
232+
233+
[Test]
234+
public async Task CanSelectRandomDoubleAsync()
235+
{
236+
using (var spy = new SqlLogSpy())
237+
{
238+
var random = new Random();
239+
var x =
240+
await (db
241+
.Orders.Select(o => new { id = o.OrderId, r = random.NextDouble() })
242+
.OrderBy(o => o.id).ToListAsync());
243+
244+
Assert.That(x, Has.Count.GreaterThan(0));
245+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
246+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(1));
247+
248+
if (!LegacyPreEvaluation && IsFunctionSupported("random"))
249+
{
250+
// Naïve randomness check
251+
Assert.That(
252+
randomValues,
253+
Has.Length.GreaterThan(x.Count / 2),
254+
"Generated values do not seem very random");
255+
}
256+
257+
AssertFunctionInSql("random", spy);
258+
}
259+
}
260+
261+
[Test]
262+
public async Task CanQueryByRandomIntAsync()
263+
{
264+
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
265+
using (var spy = new SqlLogSpy())
266+
{
267+
var random = new Random();
268+
var x = await (db.Orders.CountAsync(o => o.OrderId - idMin - 1 < random.Next()));
269+
270+
Assert.That(x, Is.GreaterThan(0));
271+
// Next requires support of both floor and rand
272+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
273+
}
274+
}
275+
276+
[Test]
277+
public async Task CanSelectRandomIntAsync()
278+
{
279+
using (var spy = new SqlLogSpy())
280+
{
281+
var random = new Random();
282+
var x =
283+
await (db
284+
.Orders.Select(o => new { id = o.OrderId, r = random.Next() })
285+
.OrderBy(o => o.id).ToListAsync());
286+
287+
Assert.That(x, Has.Count.GreaterThan(0));
288+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
289+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(int.MaxValue).And.TypeOf<int>());
290+
291+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
292+
{
293+
// Naïve randomness check
294+
Assert.That(
295+
randomValues,
296+
Has.Length.GreaterThan(x.Count / 2),
297+
"Generated values do not seem very random");
298+
}
299+
300+
// Next requires support of both floor and rand
301+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
302+
}
303+
}
304+
305+
[Test]
306+
public async Task CanQueryByRandomIntWithMaxAsync()
307+
{
308+
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
309+
using (var spy = new SqlLogSpy())
310+
{
311+
var random = new Random();
312+
var x = await (db.Orders.CountAsync(o => o.OrderId - idMin - 1 < random.Next(10)));
313+
314+
Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
315+
// Next requires support of both floor and rand
316+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
317+
}
318+
}
319+
320+
[Test]
321+
public async Task CanSelectRandomIntWithMaxAsync()
322+
{
323+
using (var spy = new SqlLogSpy())
324+
{
325+
var random = new Random();
326+
var x =
327+
await (db
328+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(10) })
329+
.OrderBy(o => o.id).ToListAsync());
330+
331+
Assert.That(x, Has.Count.GreaterThan(0));
332+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
333+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(10).And.TypeOf<int>());
334+
335+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
336+
{
337+
// Naïve randomness check
338+
Assert.That(
339+
randomValues,
340+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
341+
"Generated values do not seem very random");
342+
}
343+
344+
// Next requires support of both floor and rand
345+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
346+
}
347+
}
348+
349+
[Test]
350+
public async Task CanQueryByRandomIntWithMinMaxAsync()
351+
{
352+
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
353+
using (var spy = new SqlLogSpy())
354+
{
355+
var random = new Random();
356+
var x = await (db.Orders.CountAsync(o => o.OrderId - idMin < random.Next(1, 11)));
357+
358+
Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
359+
// Next requires support of both floor and rand
360+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
361+
}
362+
}
363+
364+
[Test]
365+
public async Task CanSelectRandomIntWithMinMaxAsync()
366+
{
367+
using (var spy = new SqlLogSpy())
368+
{
369+
var random = new Random();
370+
var x =
371+
await (db
372+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(1, 11) })
373+
.OrderBy(o => o.id).ToListAsync());
374+
375+
Assert.That(x, Has.Count.GreaterThan(0));
376+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
377+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(1).And.LessThan(11).And.TypeOf<int>());
378+
379+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
380+
{
381+
// Naïve randomness check
382+
Assert.That(
383+
randomValues,
384+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
385+
"Generated values do not seem very random");
386+
}
387+
388+
// Next requires support of both floor and rand
389+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
390+
}
391+
}
392+
220393
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
221394
{
222395
if (!IsFunctionSupported(functionName))

src/NHibernate.Test/Linq/PreEvaluationTests.cs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,179 @@ public void CanSelectNewGuid()
205205
}
206206
}
207207

208+
[Test]
209+
public void CanQueryByRandomDouble()
210+
{
211+
using (var spy = new SqlLogSpy())
212+
{
213+
var random = new Random();
214+
var x = db.Orders.Count(o => o.OrderId > random.NextDouble());
215+
216+
Assert.That(x, Is.GreaterThan(0));
217+
AssertFunctionInSql("random", spy);
218+
}
219+
}
220+
221+
[Test]
222+
public void CanSelectRandomDouble()
223+
{
224+
using (var spy = new SqlLogSpy())
225+
{
226+
var random = new Random();
227+
var x =
228+
db
229+
.Orders.Select(o => new { id = o.OrderId, r = random.NextDouble() })
230+
.OrderBy(o => o.id).ToList();
231+
232+
Assert.That(x, Has.Count.GreaterThan(0));
233+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
234+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(1));
235+
236+
if (!LegacyPreEvaluation && IsFunctionSupported("random"))
237+
{
238+
// Naïve randomness check
239+
Assert.That(
240+
randomValues,
241+
Has.Length.GreaterThan(x.Count / 2),
242+
"Generated values do not seem very random");
243+
}
244+
245+
AssertFunctionInSql("random", spy);
246+
}
247+
}
248+
249+
[Test]
250+
public void CanQueryByRandomInt()
251+
{
252+
var idMin = db.Orders.Min(o => o.OrderId);
253+
using (var spy = new SqlLogSpy())
254+
{
255+
var random = new Random();
256+
var x = db.Orders.Count(o => o.OrderId - idMin - 1 < random.Next());
257+
258+
Assert.That(x, Is.GreaterThan(0));
259+
// Next requires support of both floor and rand
260+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
261+
}
262+
}
263+
264+
[Test]
265+
public void CanSelectRandomInt()
266+
{
267+
using (var spy = new SqlLogSpy())
268+
{
269+
var random = new Random();
270+
var x =
271+
db
272+
.Orders.Select(o => new { id = o.OrderId, r = random.Next() })
273+
.OrderBy(o => o.id).ToList();
274+
275+
Assert.That(x, Has.Count.GreaterThan(0));
276+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
277+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(int.MaxValue).And.TypeOf<int>());
278+
279+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
280+
{
281+
// Naïve randomness check
282+
Assert.That(
283+
randomValues,
284+
Has.Length.GreaterThan(x.Count / 2),
285+
"Generated values do not seem very random");
286+
}
287+
288+
// Next requires support of both floor and rand
289+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
290+
}
291+
}
292+
293+
[Test]
294+
public void CanQueryByRandomIntWithMax()
295+
{
296+
var idMin = db.Orders.Min(o => o.OrderId);
297+
using (var spy = new SqlLogSpy())
298+
{
299+
var random = new Random();
300+
var x = db.Orders.Count(o => o.OrderId - idMin - 1 < random.Next(10));
301+
302+
Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
303+
// Next requires support of both floor and rand
304+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
305+
}
306+
}
307+
308+
[Test]
309+
public void CanSelectRandomIntWithMax()
310+
{
311+
using (var spy = new SqlLogSpy())
312+
{
313+
var random = new Random();
314+
var x =
315+
db
316+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(10) })
317+
.OrderBy(o => o.id).ToList();
318+
319+
Assert.That(x, Has.Count.GreaterThan(0));
320+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
321+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(10).And.TypeOf<int>());
322+
323+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
324+
{
325+
// Naïve randomness check
326+
Assert.That(
327+
randomValues,
328+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
329+
"Generated values do not seem very random");
330+
}
331+
332+
// Next requires support of both floor and rand
333+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
334+
}
335+
}
336+
337+
[Test]
338+
public void CanQueryByRandomIntWithMinMax()
339+
{
340+
var idMin = db.Orders.Min(o => o.OrderId);
341+
using (var spy = new SqlLogSpy())
342+
{
343+
var random = new Random();
344+
var x = db.Orders.Count(o => o.OrderId - idMin < random.Next(1, 11));
345+
346+
Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
347+
// Next requires support of both floor and rand
348+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
349+
}
350+
}
351+
352+
[Test]
353+
public void CanSelectRandomIntWithMinMax()
354+
{
355+
using (var spy = new SqlLogSpy())
356+
{
357+
var random = new Random();
358+
var x =
359+
db
360+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(1, 11) })
361+
.OrderBy(o => o.id).ToList();
362+
363+
Assert.That(x, Has.Count.GreaterThan(0));
364+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
365+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(1).And.LessThan(11).And.TypeOf<int>());
366+
367+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
368+
{
369+
// Naïve randomness check
370+
Assert.That(
371+
randomValues,
372+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
373+
"Generated values do not seem very random");
374+
}
375+
376+
// Next requires support of both floor and rand
377+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
378+
}
379+
}
380+
208381
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
209382
{
210383
if (!IsFunctionSupported(functionName))

src/NHibernate/Dialect/DB2Dialect.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public DB2Dialect()
7777
RegisterFunction("log10", new StandardSQLFunction("log10", NHibernateUtil.Double));
7878
RegisterFunction("radians", new StandardSQLFunction("radians", NHibernateUtil.Double));
7979
RegisterFunction("rand", new NoArgSQLFunction("rand", NHibernateUtil.Double));
80+
RegisterFunction("random", new NoArgSQLFunction("rand", NHibernateUtil.Double));
8081
RegisterFunction("sin", new StandardSQLFunction("sin", NHibernateUtil.Double));
8182
RegisterFunction("soundex", new StandardSQLFunction("soundex", NHibernateUtil.String));
8283
RegisterFunction("sqrt", new StandardSQLFunction("sqrt", NHibernateUtil.Double));

src/NHibernate/Dialect/FirebirdDialect.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ private void RegisterMathematicalFunctions()
464464
RegisterFunction("log10", new StandardSQLFunction("log10", NHibernateUtil.Double));
465465
RegisterFunction("pi", new NoArgSQLFunction("pi", NHibernateUtil.Double));
466466
RegisterFunction("rand", new NoArgSQLFunction("rand", NHibernateUtil.Double));
467+
RegisterFunction("random", new NoArgSQLFunction("rand", NHibernateUtil.Double));
467468
RegisterFunction("sign", new StandardSQLFunction("sign", NHibernateUtil.Int32));
468469
RegisterFunction("sqtr", new StandardSQLFunction("sqtr", NHibernateUtil.Double));
469470
RegisterFunction("trunc", new StandardSQLFunction("trunc"));

src/NHibernate/Dialect/HanaDialectBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ protected virtual void RegisterNHibernateFunctions()
396396
RegisterFunction("sysdate", new NoArgSQLFunction("current_timestamp", NHibernateUtil.DateTime, false));
397397
RegisterFunction("truncate", new SQLFunctionTemplateWithRequiredParameters(null, "floor(?1 * power(10, ?2)) / power(10, ?2)", new object[] { null, "0" }));
398398
RegisterFunction("new_uuid", new NoArgSQLFunction("sysuuid", NHibernateUtil.Guid, false));
399+
RegisterFunction("random", new NoArgSQLFunction("rand", NHibernateUtil.Double));
399400
}
400401

401402
protected virtual void RegisterHANAFunctions()

0 commit comments

Comments
 (0)