Skip to content

Commit 84e00a6

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

18 files changed

+573
-7
lines changed

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

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,197 @@ public async Task CanSelectNewGuidAsync()
386386
Assert.Fail("The test should have thrown a QueryException, but has not thrown anything");
387387
}
388388

389+
[Test]
390+
public async Task CanQueryByRandomDoubleAsync()
391+
{
392+
using (var spy = new SqlLogSpy())
393+
{
394+
var random = new Random();
395+
var x = await (db.Orders.CountAsync(o => o.OrderId > random.NextDouble()));
396+
397+
Assert.That(x, Is.GreaterThan(0));
398+
AssertFunctionInSql("random", spy);
399+
}
400+
}
401+
402+
[Test]
403+
public async Task CanSelectRandomDoubleAsync()
404+
{
405+
using (var spy = new SqlLogSpy())
406+
{
407+
var random = new Random();
408+
var x =
409+
await (db
410+
.Orders.Select(o => new { id = o.OrderId, r = random.NextDouble() })
411+
.OrderBy(o => o.id).ToListAsync());
412+
413+
Assert.That(x, Has.Count.GreaterThan(0));
414+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
415+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(1));
416+
417+
if (!LegacyPreEvaluation && IsFunctionSupported("random"))
418+
{
419+
// Naïve randomness check
420+
Assert.That(
421+
randomValues,
422+
Has.Length.GreaterThan(x.Count / 2),
423+
"Generated values do not seem very random");
424+
}
425+
426+
AssertFunctionInSql("random", spy);
427+
}
428+
}
429+
430+
[Test]
431+
public async Task CanQueryByRandomIntAsync()
432+
{
433+
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
434+
using (var spy = new SqlLogSpy())
435+
{
436+
var random = new Random();
437+
// Dodge a Firebird driver limitation by putting the constants before the order id.
438+
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
439+
// knowing the type of the condition. For some reasons the driver considers the casting should not be
440+
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
441+
// Firebird complain, so moving the constants on the left side have been put before the order id, in
442+
// order for these constants to be casted by the driver.
443+
var x = await (db.Orders.CountAsync(o => -idMin - 1 + o.OrderId < random.Next()));
444+
445+
Assert.That(x, Is.GreaterThan(0));
446+
// Next requires support of both floor and rand
447+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
448+
}
449+
}
450+
451+
[Test]
452+
public async Task CanSelectRandomIntAsync()
453+
{
454+
using (var spy = new SqlLogSpy())
455+
{
456+
var random = new Random();
457+
var x =
458+
await (db
459+
.Orders.Select(o => new { id = o.OrderId, r = random.Next() })
460+
.OrderBy(o => o.id).ToListAsync());
461+
462+
Assert.That(x, Has.Count.GreaterThan(0));
463+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
464+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(int.MaxValue).And.TypeOf<int>());
465+
466+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
467+
{
468+
// Naïve randomness check
469+
Assert.That(
470+
randomValues,
471+
Has.Length.GreaterThan(x.Count / 2),
472+
"Generated values do not seem very random");
473+
}
474+
475+
// Next requires support of both floor and rand
476+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
477+
}
478+
}
479+
480+
[Test]
481+
public async Task CanQueryByRandomIntWithMaxAsync()
482+
{
483+
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
484+
using (var spy = new SqlLogSpy())
485+
{
486+
var random = new Random();
487+
// Dodge a Firebird driver limitation by putting the constants before the order id.
488+
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
489+
// knowing the type of the condition. For some reasons the driver considers the casting should not be
490+
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
491+
// Firebird complain, so moving the constants on the left side have been put before the order id, in
492+
// order for these constants to be casted by the driver.
493+
var x = await (db.Orders.CountAsync(o => -idMin + o.OrderId <= random.Next(10)));
494+
495+
Assert.That(x, Is.GreaterThan(0).And.LessThan(11));
496+
// Next requires support of both floor and rand
497+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
498+
}
499+
}
500+
501+
[Test]
502+
public async Task CanSelectRandomIntWithMaxAsync()
503+
{
504+
using (var spy = new SqlLogSpy())
505+
{
506+
var random = new Random();
507+
var x =
508+
await (db
509+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(10) })
510+
.OrderBy(o => o.id).ToListAsync());
511+
512+
Assert.That(x, Has.Count.GreaterThan(0));
513+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
514+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(10).And.TypeOf<int>());
515+
516+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
517+
{
518+
// Naïve randomness check
519+
Assert.That(
520+
randomValues,
521+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
522+
"Generated values do not seem very random");
523+
}
524+
525+
// Next requires support of both floor and rand
526+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
527+
}
528+
}
529+
530+
[Test]
531+
public async Task CanQueryByRandomIntWithMinMaxAsync()
532+
{
533+
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
534+
using (var spy = new SqlLogSpy())
535+
{
536+
var random = new Random();
537+
// Dodge a Firebird driver limitation by putting the constants before the order id.
538+
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
539+
// knowing the type of the condition. For some reasons the driver considers the casting should not be
540+
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
541+
// Firebird complain, so moving the constants on the left side have been put before the order id, in
542+
// order for these constants to be casted by the driver.
543+
var x = await (db.Orders.CountAsync(o => -idMin + o.OrderId < random.Next(1, 10)));
544+
545+
Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
546+
// Next requires support of both floor and rand
547+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
548+
}
549+
}
550+
551+
[Test]
552+
public async Task CanSelectRandomIntWithMinMaxAsync()
553+
{
554+
using (var spy = new SqlLogSpy())
555+
{
556+
var random = new Random();
557+
var x =
558+
await (db
559+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(1, 11) })
560+
.OrderBy(o => o.id).ToListAsync());
561+
562+
Assert.That(x, Has.Count.GreaterThan(0));
563+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
564+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(1).And.LessThan(11).And.TypeOf<int>());
565+
566+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
567+
{
568+
// Naïve randomness check
569+
Assert.That(
570+
randomValues,
571+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
572+
"Generated values do not seem very random");
573+
}
574+
575+
// Next requires support of both floor and rand
576+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
577+
}
578+
}
579+
389580
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
390581
{
391582
if (!IsFunctionSupported(functionName))

src/NHibernate.Test/Linq/PreEvaluationTests.cs

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,197 @@ public void CanSelectNewGuid()
374374
Assert.Fail("The test should have thrown a QueryException, but has not thrown anything");
375375
}
376376

377+
[Test]
378+
public void CanQueryByRandomDouble()
379+
{
380+
using (var spy = new SqlLogSpy())
381+
{
382+
var random = new Random();
383+
var x = db.Orders.Count(o => o.OrderId > random.NextDouble());
384+
385+
Assert.That(x, Is.GreaterThan(0));
386+
AssertFunctionInSql("random", spy);
387+
}
388+
}
389+
390+
[Test]
391+
public void CanSelectRandomDouble()
392+
{
393+
using (var spy = new SqlLogSpy())
394+
{
395+
var random = new Random();
396+
var x =
397+
db
398+
.Orders.Select(o => new { id = o.OrderId, r = random.NextDouble() })
399+
.OrderBy(o => o.id).ToList();
400+
401+
Assert.That(x, Has.Count.GreaterThan(0));
402+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
403+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(1));
404+
405+
if (!LegacyPreEvaluation && IsFunctionSupported("random"))
406+
{
407+
// Naïve randomness check
408+
Assert.That(
409+
randomValues,
410+
Has.Length.GreaterThan(x.Count / 2),
411+
"Generated values do not seem very random");
412+
}
413+
414+
AssertFunctionInSql("random", spy);
415+
}
416+
}
417+
418+
[Test]
419+
public void CanQueryByRandomInt()
420+
{
421+
var idMin = db.Orders.Min(o => o.OrderId);
422+
using (var spy = new SqlLogSpy())
423+
{
424+
var random = new Random();
425+
// Dodge a Firebird driver limitation by putting the constants before the order id.
426+
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
427+
// knowing the type of the condition. For some reasons the driver considers the casting should not be
428+
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
429+
// Firebird complain, so moving the constants on the left side have been put before the order id, in
430+
// order for these constants to be casted by the driver.
431+
var x = db.Orders.Count(o => -idMin - 1 + o.OrderId < random.Next());
432+
433+
Assert.That(x, Is.GreaterThan(0));
434+
// Next requires support of both floor and rand
435+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
436+
}
437+
}
438+
439+
[Test]
440+
public void CanSelectRandomInt()
441+
{
442+
using (var spy = new SqlLogSpy())
443+
{
444+
var random = new Random();
445+
var x =
446+
db
447+
.Orders.Select(o => new { id = o.OrderId, r = random.Next() })
448+
.OrderBy(o => o.id).ToList();
449+
450+
Assert.That(x, Has.Count.GreaterThan(0));
451+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
452+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(int.MaxValue).And.TypeOf<int>());
453+
454+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
455+
{
456+
// Naïve randomness check
457+
Assert.That(
458+
randomValues,
459+
Has.Length.GreaterThan(x.Count / 2),
460+
"Generated values do not seem very random");
461+
}
462+
463+
// Next requires support of both floor and rand
464+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
465+
}
466+
}
467+
468+
[Test]
469+
public void CanQueryByRandomIntWithMax()
470+
{
471+
var idMin = db.Orders.Min(o => o.OrderId);
472+
using (var spy = new SqlLogSpy())
473+
{
474+
var random = new Random();
475+
// Dodge a Firebird driver limitation by putting the constants before the order id.
476+
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
477+
// knowing the type of the condition. For some reasons the driver considers the casting should not be
478+
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
479+
// Firebird complain, so moving the constants on the left side have been put before the order id, in
480+
// order for these constants to be casted by the driver.
481+
var x = db.Orders.Count(o => -idMin + o.OrderId <= random.Next(10));
482+
483+
Assert.That(x, Is.GreaterThan(0).And.LessThan(11));
484+
// Next requires support of both floor and rand
485+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
486+
}
487+
}
488+
489+
[Test]
490+
public void CanSelectRandomIntWithMax()
491+
{
492+
using (var spy = new SqlLogSpy())
493+
{
494+
var random = new Random();
495+
var x =
496+
db
497+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(10) })
498+
.OrderBy(o => o.id).ToList();
499+
500+
Assert.That(x, Has.Count.GreaterThan(0));
501+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
502+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(10).And.TypeOf<int>());
503+
504+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
505+
{
506+
// Naïve randomness check
507+
Assert.That(
508+
randomValues,
509+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
510+
"Generated values do not seem very random");
511+
}
512+
513+
// Next requires support of both floor and rand
514+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
515+
}
516+
}
517+
518+
[Test]
519+
public void CanQueryByRandomIntWithMinMax()
520+
{
521+
var idMin = db.Orders.Min(o => o.OrderId);
522+
using (var spy = new SqlLogSpy())
523+
{
524+
var random = new Random();
525+
// Dodge a Firebird driver limitation by putting the constants before the order id.
526+
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
527+
// knowing the type of the condition. For some reasons the driver considers the casting should not be
528+
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
529+
// Firebird complain, so moving the constants on the left side have been put before the order id, in
530+
// order for these constants to be casted by the driver.
531+
var x = db.Orders.Count(o => -idMin + o.OrderId < random.Next(1, 10));
532+
533+
Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
534+
// Next requires support of both floor and rand
535+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
536+
}
537+
}
538+
539+
[Test]
540+
public void CanSelectRandomIntWithMinMax()
541+
{
542+
using (var spy = new SqlLogSpy())
543+
{
544+
var random = new Random();
545+
var x =
546+
db
547+
.Orders.Select(o => new { id = o.OrderId, r = random.Next(1, 11) })
548+
.OrderBy(o => o.id).ToList();
549+
550+
Assert.That(x, Has.Count.GreaterThan(0));
551+
var randomValues = x.Select(o => o.r).Distinct().ToArray();
552+
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(1).And.LessThan(11).And.TypeOf<int>());
553+
554+
if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
555+
{
556+
// Naïve randomness check
557+
Assert.That(
558+
randomValues,
559+
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
560+
"Generated values do not seem very random");
561+
}
562+
563+
// Next requires support of both floor and rand
564+
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
565+
}
566+
}
567+
377568
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
378569
{
379570
if (!IsFunctionSupported(functionName))

0 commit comments

Comments
 (0)