Skip to content

Commit

Permalink
fixup! Support evaluation of Random.Next and NextDouble on db side
Browse files Browse the repository at this point in the history
Add an option for failing or falling back in case of lack of dialect
support
  • Loading branch information
fredericDelaporte committed Nov 11, 2019
1 parent 3a6a13f commit e565704
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 311 deletions.
221 changes: 51 additions & 170 deletions src/NHibernate.Test/Async/Linq/PreEvaluationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,195 +43,76 @@ protected override void Configure(Configuration configuration)
configuration.SetProperty(Environment.LinqToHqlFallbackOnPreEvaluation, FallbackOnPreEvaluation.ToString());
}

[Test]
public async Task CanQueryByRandomDoubleAsync()
{
using (var spy = new SqlLogSpy())
{
var random = new Random();
var x = await (db.Orders.CountAsync(o => o.OrderId > random.NextDouble()));

Assert.That(x, Is.GreaterThan(0));
AssertFunctionInSql("random", spy);
}
}

[Test]
public async Task CanSelectRandomDoubleAsync()
{
using (var spy = new SqlLogSpy())
{
var random = new Random();
var x =
await (db
.Orders.Select(o => new { id = o.OrderId, r = random.NextDouble() })
.OrderBy(o => o.id).ToListAsync());

Assert.That(x, Has.Count.GreaterThan(0));
var randomValues = x.Select(o => o.r).Distinct().ToArray();
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(1));

if (!LegacyPreEvaluation && IsFunctionSupported("random"))
{
// Naïve randomness check
Assert.That(
randomValues,
Has.Length.GreaterThan(x.Count / 2),
"Generated values do not seem very random");
}

AssertFunctionInSql("random", spy);
}
}

[Test]
public async Task CanQueryByRandomIntAsync()
{
var isSupported = IsFunctionSupported("random") && IsFunctionSupported("floor");
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
using (var spy = new SqlLogSpy())
{
var random = new Random();
// Dodge a Firebird driver limitation by putting the constants before the order id.
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
// knowing the type of the condition. For some reasons the driver considers the casting should not be
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
// Firebird complain, so moving the constants on the left side have been put before the order id, in
// order for these constants to be casted by the driver.
var x = await (db.Orders.CountAsync(o => -idMin - 1 + o.OrderId < random.Next()));

Assert.That(x, Is.GreaterThan(0));
// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
}
}

[Test]
public async Task CanSelectRandomIntAsync()
{
using (var spy = new SqlLogSpy())
{
var random = new Random();
var x =
await (db
.Orders.Select(o => new { id = o.OrderId, r = random.Next() })
.OrderBy(o => o.id).ToListAsync());

Assert.That(x, Has.Count.GreaterThan(0));
var randomValues = x.Select(o => o.r).Distinct().ToArray();
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(int.MaxValue).And.TypeOf<int>());

if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
RunTest(
isSupported,
spy =>
{
// Naïve randomness check
Assert.That(
randomValues,
Has.Length.GreaterThan(x.Count / 2),
"Generated values do not seem very random");
}

// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
}
var random = new Random();
// Dodge a Firebird driver limitation by putting the constants before the order id.
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
// knowing the type of the condition. For some reasons the driver considers the casting should not be
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
// Firebird complain, so moving the constants on the left side have been put before the order id, in
// order for these constants to be casted by the driver.
var x = db.Orders.Count(o => -idMin - 1 + o.OrderId < random.Next());
Assert.That(x, Is.GreaterThan(0));
// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
});
}

[Test]
public async Task CanQueryByRandomIntWithMaxAsync()
{
var isSupported = IsFunctionSupported("random") && IsFunctionSupported("floor");
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
using (var spy = new SqlLogSpy())
{
var random = new Random();
// Dodge a Firebird driver limitation by putting the constants before the order id.
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
// knowing the type of the condition. For some reasons the driver considers the casting should not be
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
// Firebird complain, so moving the constants on the left side have been put before the order id, in
// order for these constants to be casted by the driver.
var x = await (db.Orders.CountAsync(o => -idMin + o.OrderId <= random.Next(10)));

Assert.That(x, Is.GreaterThan(0).And.LessThan(11));
// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
}
}

[Test]
public async Task CanSelectRandomIntWithMaxAsync()
{
using (var spy = new SqlLogSpy())
{
var random = new Random();
var x =
await (db
.Orders.Select(o => new { id = o.OrderId, r = random.Next(10) })
.OrderBy(o => o.id).ToListAsync());

Assert.That(x, Has.Count.GreaterThan(0));
var randomValues = x.Select(o => o.r).Distinct().ToArray();
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(0).And.LessThan(10).And.TypeOf<int>());

if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
RunTest(
isSupported,
spy =>
{
// Naïve randomness check
Assert.That(
randomValues,
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
"Generated values do not seem very random");
}

// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
}
var random = new Random();
// Dodge a Firebird driver limitation by putting the constants before the order id.
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
// knowing the type of the condition. For some reasons the driver considers the casting should not be
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
// Firebird complain, so moving the constants on the left side have been put before the order id, in
// order for these constants to be casted by the driver.
var x = db.Orders.Count(o => -idMin + o.OrderId <= random.Next(10));
Assert.That(x, Is.GreaterThan(0).And.LessThan(11));
// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
});
}

[Test]
public async Task CanQueryByRandomIntWithMinMaxAsync()
{
var isSupported = IsFunctionSupported("random") && IsFunctionSupported("floor");
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
using (var spy = new SqlLogSpy())
{
var random = new Random();
// Dodge a Firebird driver limitation by putting the constants before the order id.
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
// knowing the type of the condition. For some reasons the driver considers the casting should not be
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
// Firebird complain, so moving the constants on the left side have been put before the order id, in
// order for these constants to be casted by the driver.
var x = await (db.Orders.CountAsync(o => -idMin + o.OrderId < random.Next(1, 10)));

Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
}
}

[Test]
public async Task CanSelectRandomIntWithMinMaxAsync()
{
using (var spy = new SqlLogSpy())
{
var random = new Random();
var x =
await (db
.Orders.Select(o => new { id = o.OrderId, r = random.Next(1, 11) })
.OrderBy(o => o.id).ToListAsync());

Assert.That(x, Has.Count.GreaterThan(0));
var randomValues = x.Select(o => o.r).Distinct().ToArray();
Assert.That(randomValues, Has.All.GreaterThanOrEqualTo(1).And.LessThan(11).And.TypeOf<int>());

if (!LegacyPreEvaluation && IsFunctionSupported("random") && IsFunctionSupported("floor"))
RunTest(
isSupported,
spy =>
{
// Naïve randomness check
Assert.That(
randomValues,
Has.Length.GreaterThan(Math.Min(10, x.Count) / 2),
"Generated values do not seem very random");
}

// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
}
var random = new Random();
// Dodge a Firebird driver limitation by putting the constants before the order id.
// This driver cast parameters to their types in some cases for avoiding Firebird complaining of not
// knowing the type of the condition. For some reasons the driver considers the casting should not be
// done next to the conditional operator. Having the cast only on one side is enough for avoiding
// Firebird complain, so moving the constants on the left side have been put before the order id, in
// order for these constants to be casted by the driver.
var x = db.Orders.Count(o => -idMin + o.OrderId < random.Next(1, 10));
Assert.That(x, Is.GreaterThan(0).And.LessThan(10));
// Next requires support of both floor and rand
AssertFunctionInSql(IsFunctionSupported("floor") ? "random" : "floor", spy);
});
}

private void RunTest(bool isSupported, Action<SqlLogSpy> test)
Expand Down
Loading

0 comments on commit e565704

Please sign in to comment.