Skip to content

Commit

Permalink
Merge pull request #1868 from fredericDelaporte/DbSideNowGuidRandom
Browse files Browse the repository at this point in the history
Support DB side methods
  • Loading branch information
fredericDelaporte committed Mar 22, 2020
2 parents 6c252ee + 3a8a1ba commit 450313b
Show file tree
Hide file tree
Showing 50 changed files with 1,518 additions and 56 deletions.
51 changes: 51 additions & 0 deletions doc/reference/modules/configuration.xml
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,57 @@ var session = sessions.OpenSession(conn);
</para>
</entry>
</row>
<row>
<entry>
<literal>linqtohql.legacy_preevaluation</literal>
</entry>
<entry>
Whether to use the legacy pre-evaluation or not in Linq queries. Defaults to <literal>true</literal>.
<para>
<emphasis role="strong">eg.</emphasis>
<literal>true</literal> | <literal>false</literal>
</para>
<para>
Legacy pre-evaluation is causing special properties or functions like <literal>DateTime.Now</literal>
or <literal>Guid.NewGuid()</literal> to be always evaluated with the .Net runtime and replaced in the
query by parameter values.
</para>
<para>
The new pre-evaluation allows them to be converted to HQL function calls which will be run on the db
side. This allows for example to retrieve the server time instead of the client time, or to generate
UUIDs for each row instead of an unique one for all rows.
</para>
<para>
The new pre-evaluation will likely be enabled by default in the next major version (6.0).
</para>
</entry>
</row>
<row>
<entry>
<literal>linqtohql.fallback_on_preevaluation</literal>
</entry>
<entry>
When the new pre-evaluation is enabled, should methods which translation is not supported by the current
dialect fallback to pre-evaluation? Defaults to <literal>false</literal>.
<para>
<emphasis role="strong">eg.</emphasis>
<literal>true</literal> | <literal>false</literal>
</para>
<para>
When this fallback option is enabled while legacy pre-evaluation is disabled, properties or functions
like <literal>DateTime.Now</literal> or <literal>Guid.NewGuid()</literal> used in Linq expressions
will not fail when the dialect does not support them, but will instead be pre-evaluated.
</para>
<para>
When this fallback option is disabled while legacy pre-evaluation is disabled, properties or functions
like <literal>DateTime.Now</literal> or <literal>Guid.NewGuid()</literal> used in Linq expressions
will fail when the dialect does not support them.
</para>
<para>
This option has no effect if the legacy pre-evaluation is enabled.
</para>
</entry>
</row>
<row>
<entry>
<literal>sql_exception_converter</literal>
Expand Down
3 changes: 2 additions & 1 deletion src/NHibernate.Test/Async/Linq/MiscellaneousTextFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public class MiscellaneousTextFixtureAsync : LinqTestCase
[Test(Description = "This sample uses Count to find the number of Orders placed before yesterday in the database.")]
public async Task CountWithWhereClauseAsync()
{
var q = from o in db.Orders where o.OrderDate <= DateTime.Today.AddDays(-1) select o;
var yesterday = DateTime.Today.AddDays(-1);
var q = from o in db.Orders where o.OrderDate <= yesterday select o;

var count = await (q.CountAsync());

Expand Down
152 changes: 152 additions & 0 deletions src/NHibernate.Test/Async/Linq/PreEvaluationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Cfg;
using NHibernate.SqlTypes;
using NUnit.Framework;
using Environment = NHibernate.Cfg.Environment;
using NHibernate.Linq;

namespace NHibernate.Test.Linq
{
using System.Threading.Tasks;
[TestFixture(false, false)]
[TestFixture(true, false)]
[TestFixture(false, true)]
public class PreEvaluationTestsAsync : LinqTestCase
{
private readonly bool LegacyPreEvaluation;
private readonly bool FallbackOnPreEvaluation;

public PreEvaluationTestsAsync(bool legacy, bool fallback)
{
LegacyPreEvaluation = legacy;
FallbackOnPreEvaluation = fallback;
}

protected override void Configure(Configuration configuration)
{
base.Configure(configuration);

configuration.SetProperty(Environment.FormatSql, "false");
configuration.SetProperty(Environment.LinqToHqlLegacyPreEvaluation, LegacyPreEvaluation.ToString());
configuration.SetProperty(Environment.LinqToHqlFallbackOnPreEvaluation, FallbackOnPreEvaluation.ToString());
}

private void RunTest(bool isSupported, Action<SqlLogSpy> test)
{
using (var spy = new SqlLogSpy())
{
try
{
test(spy);
}
catch (QueryException)
{
if (!isSupported && !FallbackOnPreEvaluation)
// Expected failure
return;
throw;
}
}

if (!isSupported && !FallbackOnPreEvaluation)
Assert.Fail("The test should have thrown a QueryException, but has not thrown anything");
}

[Test]
public async Task CanQueryByRandomIntAsync()
{
var isSupported = IsFunctionSupported("random") && IsFunctionSupported("floor");
var idMin = await (db.Orders.MinAsync(o => o.OrderId));
RunTest(
isSupported,
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));
RunTest(
isSupported,
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));
RunTest(
isSupported,
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 AssertFunctionInSql(string functionName, SqlLogSpy spy)
{
if (!IsFunctionSupported(functionName))
Assert.Inconclusive($"{functionName} is not supported by the dialect");

var function = Dialect.Functions[functionName].Render(new List<object>(), Sfi).ToString();

if (LegacyPreEvaluation)
Assert.That(spy.GetWholeLog(), Does.Not.Contain(function));
else
Assert.That(spy.GetWholeLog(), Does.Contain(function));
}
}
}
3 changes: 2 additions & 1 deletion src/NHibernate.Test/Linq/MiscellaneousTextFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ from s in db.Shippers
[Test(Description = "This sample uses Count to find the number of Orders placed before yesterday in the database.")]
public void CountWithWhereClause()
{
var q = from o in db.Orders where o.OrderDate <= DateTime.Today.AddDays(-1) select o;
var yesterday = DateTime.Today.AddDays(-1);
var q = from o in db.Orders where o.OrderDate <= yesterday select o;

var count = q.Count();

Expand Down
Loading

0 comments on commit 450313b

Please sign in to comment.