Skip to content

Commit ac6d753

Browse files
committed
Support non parameterized constants query plan caching for Linq provider
1 parent 1c313f9 commit ac6d753

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1084
-199
lines changed

src/NHibernate.Test/Async/Futures/LinqToFutureValueFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public void ToFutureValueWithSumOnEmptySetThrowsAsync()
7070
.Select(x => x.Id)
7171
.ToFutureValue(x => x.Sum());
7272

73-
Assert.That(() => personsSum.GetValueAsync(), Throws.InnerException.TypeOf<InvalidOperationException>().Or.InnerException.TypeOf<ArgumentNullException>());
73+
Assert.That(() => personsSum.GetValueAsync(), Throws.TypeOf<InvalidOperationException>().Or.InnerException.TypeOf<ArgumentNullException>());
7474
}
7575
}
7676

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ public async Task DmlPlansAreCachedAsync()
299299
}
300300

301301
[Test]
302-
public async Task PlansWithNonParameterizedConstantsAreNotCachedAsync()
302+
public async Task PlansWithNonParameterizedConstantsAreCachedAsync()
303303
{
304304
var queryPlanCacheType = typeof(QueryPlanCache);
305305

@@ -314,12 +314,12 @@ public async Task PlansWithNonParameterizedConstantsAreNotCachedAsync()
314314
select new { c.CustomerId, c.ContactName, Constant = 1 }).FirstAsync());
315315
Assert.That(
316316
cache,
317-
Has.Count.EqualTo(0),
318-
"Query plan should not be cached.");
317+
Has.Count.EqualTo(1),
318+
"Query plan should be cached.");
319319
}
320320

321321
[Test]
322-
public async Task PlansWithNonParameterizedConstantsAreNotCachedForExpandedQueryAsync()
322+
public async Task PlansWithNonParameterizedConstantsAreCachedForExpandedQueryAsync()
323323
{
324324
var queryPlanCacheType = typeof(QueryPlanCache);
325325

@@ -335,8 +335,8 @@ public async Task PlansWithNonParameterizedConstantsAreNotCachedForExpandedQuery
335335

336336
Assert.That(
337337
cache,
338-
Has.Count.EqualTo(0),
339-
"Query plan should not be cached.");
338+
Has.Count.EqualTo(2), // The second one is for the expanded expression that has two parameters
339+
"Query plan should be cached.");
340340
}
341341

342342
//GH-2298 - Different Update queries - same query cache plan

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ public async Task UsingTwoParametersInDMLDeleteAsync()
350350
{
351351
// In case of arrays linqParameterNumber and parameterNumber will be different
352352
Assert.That(
353-
GetLinqExpression(query).ParameterValuesByName.Count,
353+
GetLinqExpression(query).NamedParameters.Count,
354354
Is.EqualTo(linqParameterNumber ?? parameterNumber),
355355
"Linq expression has different number of parameters");
356356

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Linq;
12+
using NHibernate.Dialect;
13+
using NHibernate.Linq;
14+
using NSubstitute;
15+
using NSubstitute.Extensions;
16+
using NUnit.Framework;
17+
18+
namespace NHibernate.Test.Linq
19+
{
20+
using System.Threading.Tasks;
21+
[TestFixture]
22+
public class QueryPlanTestsAsync : LinqTestCase
23+
{
24+
[Test]
25+
public async Task SelectConstantShouldBeCachedAsync()
26+
{
27+
ClearQueryPlanCache();
28+
29+
var c1 = await (db.Customers.Select(o => new {o.CustomerId, Constant = "constant"}).FirstAsync());
30+
var c2 = await (db.Customers.Select(o => new {o.CustomerId, Constant = "constant2"}).FirstAsync());
31+
var constant = "constant3";
32+
var c3 = await (db.Customers.Select(o => new {o.CustomerId, Constant = constant}).FirstAsync());
33+
constant = "constant4";
34+
var c4 = await (db.Customers.Select(o => new {o.CustomerId, Constant = constant}).FirstAsync());
35+
36+
var queryCache = GetQueryPlanCache();
37+
Assert.That(queryCache.Count, Is.EqualTo(1));
38+
39+
Assert.That(c1.Constant, Is.EqualTo("constant"));
40+
Assert.That(c2.Constant, Is.EqualTo("constant2"));
41+
Assert.That(c3.Constant, Is.EqualTo("constant3"));
42+
Assert.That(c4.Constant, Is.EqualTo("constant4"));
43+
}
44+
45+
[Test]
46+
public async Task GroupByConstantShouldBeCachedAsync()
47+
{
48+
ClearQueryPlanCache();
49+
50+
var c1 = await (db.Customers.GroupBy(o => new {o.CustomerId, Constant = "constant"}).Select(o => o.Key).FirstAsync());
51+
var c2 = await (db.Customers.GroupBy(o => new {o.CustomerId, Constant = "constant2"}).Select(o => o.Key).FirstAsync());
52+
var constant = "constant3";
53+
var c3 = await (db.Customers.GroupBy(o => new {o.CustomerId, Constant = constant}).Select(o => o.Key).FirstAsync());
54+
constant = "constant4";
55+
var c4 = await (db.Customers.GroupBy(o => new {o.CustomerId, Constant = constant}).Select(o => o.Key).FirstAsync());
56+
57+
var queryCache = GetQueryPlanCache();
58+
Assert.That(queryCache.Count, Is.EqualTo(1));
59+
60+
Assert.That(c1.Constant, Is.EqualTo("constant"));
61+
Assert.That(c2.Constant, Is.EqualTo("constant2"));
62+
Assert.That(c3.Constant, Is.EqualTo("constant3"));
63+
Assert.That(c4.Constant, Is.EqualTo("constant4"));
64+
}
65+
66+
[Test]
67+
public async Task WithLockShouldBeCachedAsync()
68+
{
69+
ClearQueryPlanCache();
70+
// Limit to a few dialects where we know the "nowait" keyword is used to make life easier.
71+
Assume.That(Dialect is MsSql2000Dialect || Dialect is Oracle8iDialect || Dialect is PostgreSQL81Dialect);
72+
73+
await (db.Customers.WithLock(LockMode.Upgrade).ToListAsync());
74+
await (db.Customers.WithLock(LockMode.UpgradeNoWait).ToListAsync());
75+
var lockMode = LockMode.None;
76+
await (db.Customers.WithLock(lockMode).ToListAsync());
77+
lockMode = LockMode.Read;
78+
await (db.Customers.WithLock(lockMode).ToListAsync());
79+
80+
var queryCache = GetQueryPlanCache();
81+
Assert.That(queryCache.Count, Is.EqualTo(4));
82+
}
83+
84+
[TestCase(true)]
85+
[TestCase(false)]
86+
public async Task SkipShouldBeCachedAsync(bool supportsVariableLimit)
87+
{
88+
if (!Dialect.SupportsLimit || (supportsVariableLimit && !Dialect.SupportsVariableLimit))
89+
{
90+
Assert.Ignore();
91+
}
92+
93+
ClearQueryPlanCache();
94+
using (var substitute = SubstituteDialect())
95+
{
96+
substitute.Value.Configure().SupportsVariableLimit.Returns(supportsVariableLimit);
97+
98+
var c1 = await (db.Customers.Skip(1).ToListAsync());
99+
var c2 = await (db.Customers.Skip(2).ToListAsync());
100+
var skip = 3;
101+
var c3 = await (db.Customers.Skip(skip).ToListAsync());
102+
skip = 4;
103+
var c4 = await (db.Customers.Skip(skip).ToListAsync());
104+
105+
var queryCache = GetQueryPlanCache();
106+
Assert.That(c1.Count, Is.Not.EqualTo(c2.Count));
107+
Assert.That(c2.Count, Is.Not.EqualTo(c3.Count));
108+
Assert.That(c3.Count, Is.Not.EqualTo(c4.Count));
109+
Assert.That(queryCache.Count, Is.EqualTo(supportsVariableLimit ? 1 : 4));
110+
}
111+
}
112+
113+
[TestCase(true)]
114+
[TestCase(false)]
115+
public async Task TakeShouldBeCachedAsync(bool supportsVariableLimit)
116+
{
117+
if (!Dialect.SupportsLimit || (supportsVariableLimit && !Dialect.SupportsVariableLimit))
118+
{
119+
Assert.Ignore();
120+
}
121+
122+
ClearQueryPlanCache();
123+
using (var substitute = SubstituteDialect())
124+
{
125+
substitute.Value.Configure().SupportsVariableLimit.Returns(supportsVariableLimit);
126+
127+
var c1 = await (db.Customers.Take(1).ToListAsync());
128+
var c2 = await (db.Customers.Take(2).ToListAsync());
129+
var skip = 3;
130+
var c3 = await (db.Customers.Take(skip).ToListAsync());
131+
skip = 4;
132+
var c4 = await (db.Customers.Take(skip).ToListAsync());
133+
134+
var queryCache = GetQueryPlanCache();
135+
Assert.That(c1.Count, Is.EqualTo(1));
136+
Assert.That(c2.Count, Is.EqualTo(2));
137+
Assert.That(c3.Count, Is.EqualTo(3));
138+
Assert.That(c4.Count, Is.EqualTo(4));
139+
Assert.That(queryCache.Count, Is.EqualTo(supportsVariableLimit ? 1 : 4));
140+
}
141+
}
142+
143+
[Test]
144+
public async Task TrimFunctionShouldNotBeCachedAsync()
145+
{
146+
ClearQueryPlanCache();
147+
148+
await (db.Customers.Select(o => new {CustomerId = o.CustomerId.Trim('-')}).FirstAsync());
149+
await (db.Customers.Select(o => new {CustomerId = o.CustomerId.Trim('+')}).FirstAsync());
150+
151+
var queryCache = GetQueryPlanCache();
152+
Assert.That(queryCache.Count, Is.EqualTo(0));
153+
}
154+
155+
[Test]
156+
public async Task SubstringFunctionShouldBeCachedAsync()
157+
{
158+
ClearQueryPlanCache();
159+
160+
var queryCache = GetQueryPlanCache();
161+
var c1 = await (db.Customers.Select(o => new {Name = o.ContactName.Substring(1)}).FirstAsync());
162+
var c2 = await (db.Customers.Select(o => new {Name = o.ContactName.Substring(2)}).FirstAsync());
163+
164+
Assert.That(c1.Name, Is.Not.EqualTo(c2.Name));
165+
Assert.That(queryCache.Count, Is.EqualTo(1));
166+
167+
ClearQueryPlanCache();
168+
c1 = await (db.Customers.Select(o => new { Name = o.ContactName.Substring(1, 2) }).FirstAsync());
169+
c2 = await (db.Customers.Select(o => new { Name = o.ContactName.Substring(2, 1) }).FirstAsync());
170+
171+
Assert.That(c1.Name, Is.Not.EqualTo(c2.Name));
172+
Assert.That(queryCache.Count, Is.EqualTo(1));
173+
}
174+
}
175+
}

src/NHibernate.Test/Async/NHSpecificTest/NH3850/MainFixture.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,7 @@ public async Task LongCountObjectAsync()
930930
"Non nullable decimal max has failed");
931931
var futureNonNullableDec = dcQuery.ToFutureValue(qdc => qdc.Max(dc => dc.NonNullableDecimal));
932932
Assert.That(() => futureNonNullableDec.GetValueAsync(cancellationToken),
933-
Throws.TargetInvocationException.And.InnerException.InstanceOf<InvalidOperationException>(),
933+
Throws.InstanceOf<InvalidOperationException>(),
934934
"Future non nullable decimal max has failed");
935935
}
936936
}
@@ -1002,7 +1002,7 @@ public async Task LongCountObjectAsync()
10021002
"Non nullable decimal min has failed");
10031003
var futureNonNullableDec = dcQuery.ToFutureValue(qdc => qdc.Min(dc => dc.NonNullableDecimal));
10041004
Assert.That(() => futureNonNullableDec.GetValueAsync(cancellationToken),
1005-
Throws.TargetInvocationException.And.InnerException.InstanceOf<InvalidOperationException>(),
1005+
Throws.InstanceOf<InvalidOperationException>(),
10061006
"Future non nullable decimal min has failed");
10071007
}
10081008
}
@@ -1017,7 +1017,7 @@ public void SingleOrDefaultBBaseAsync()
10171017
var query = session.Query<DomainClassBExtendedByA>();
10181018
Assert.That(() => query.SingleOrDefaultAsync(), Throws.InvalidOperationException);
10191019
var futureQuery = query.ToFutureValue(qdc => qdc.SingleOrDefault());
1020-
Assert.That(() => futureQuery.GetValueAsync(), Throws.TargetInvocationException.And.InnerException.TypeOf<InvalidOperationException>(), "Future");
1020+
Assert.That(() => futureQuery.GetValueAsync(), Throws.InstanceOf<InvalidOperationException>(), "Future");
10211021
}
10221022
}
10231023

@@ -1050,7 +1050,7 @@ public void SingleOrDefaultCBaseAsync()
10501050
var query = session.Query<DomainClassCExtendedByD>();
10511051
Assert.That(() => query.SingleOrDefaultAsync(), Throws.InvalidOperationException);
10521052
var futureQuery = query.ToFutureValue(qdc => qdc.SingleOrDefault());
1053-
Assert.That(() => futureQuery.GetValueAsync(), Throws.TargetInvocationException.And.InnerException.TypeOf<InvalidOperationException>(), "Future");
1053+
Assert.That(() => futureQuery.GetValueAsync(), Throws.InstanceOf<InvalidOperationException>(), "Future");
10541054
}
10551055
}
10561056

@@ -1083,7 +1083,7 @@ public void SingleOrDefaultEAsync()
10831083
var query = session.Query<DomainClassE>();
10841084
Assert.That(() => query.SingleOrDefaultAsync(), Throws.InvalidOperationException);
10851085
var futureQuery = query.ToFutureValue(qdc => qdc.SingleOrDefault());
1086-
Assert.That(() => futureQuery.GetValueAsync(), Throws.TargetInvocationException.And.InnerException.TypeOf<InvalidOperationException>(), "Future");
1086+
Assert.That(() => futureQuery.GetValueAsync(), Throws.InstanceOf<InvalidOperationException>(), "Future");
10871087
}
10881088
}
10891089

@@ -1146,7 +1146,7 @@ public void SingleOrDefaultGBaseAsync()
11461146
var query = session.Query<DomainClassGExtendedByH>();
11471147
Assert.That(() => query.SingleOrDefaultAsync(), Throws.InvalidOperationException);
11481148
var futureQuery = query.ToFutureValue(qdc => qdc.SingleOrDefault());
1149-
Assert.That(() => futureQuery.GetValueAsync(), Throws.TargetInvocationException.And.InnerException.TypeOf<InvalidOperationException>(), "Future");
1149+
Assert.That(() => futureQuery.GetValueAsync(), Throws.InstanceOf<InvalidOperationException>(), "Future");
11501150
}
11511151
}
11521152

@@ -1159,7 +1159,7 @@ public void SingleOrDefaultGBaseWithNameAsync()
11591159
var query = session.Query<DomainClassGExtendedByH>();
11601160
Assert.That(() => query.SingleOrDefaultAsync(dc => dc.Name == SearchName1), Throws.InvalidOperationException);
11611161
var futureQuery = query.ToFutureValue(qdc => qdc.SingleOrDefault(dc => dc.Name == SearchName1));
1162-
Assert.That(() => futureQuery.GetValueAsync(), Throws.TargetInvocationException.And.InnerException.TypeOf<InvalidOperationException>(), "Future");
1162+
Assert.That(() => futureQuery.GetValueAsync(), Throws.InstanceOf<InvalidOperationException>(), "Future");
11631163
}
11641164
}
11651165

@@ -1172,7 +1172,7 @@ public void SingleOrDefaultObjectAsync()
11721172
var query = session.Query<object>();
11731173
Assert.That(() => query.SingleOrDefaultAsync(), Throws.InvalidOperationException);
11741174
var futureQuery = query.ToFutureValue(qdc => qdc.SingleOrDefault());
1175-
Assert.That(() => futureQuery.GetValueAsync(), Throws.TargetInvocationException.And.InnerException.TypeOf<InvalidOperationException>(), "Future");
1175+
Assert.That(() => futureQuery.GetValueAsync(), Throws.InstanceOf<InvalidOperationException>(), "Future");
11761176
}
11771177
}
11781178

@@ -1276,7 +1276,7 @@ public async Task SumObjectAsync()
12761276
"Non nullable decimal sum has failed");
12771277
var futureNonNullableDec = dcQuery.ToFutureValue(qdc => qdc.Sum(dc => dc.NonNullableDecimal));
12781278
Assert.That(() => futureNonNullableDec.GetValueAsync(cancellationToken),
1279-
Throws.TargetInvocationException.And.InnerException.InstanceOf<InvalidOperationException>(),
1279+
Throws.InstanceOf<InvalidOperationException>(),
12801280
"Future non nullable decimal sum has failed");
12811281
}
12821282
}

src/NHibernate.Test/Futures/LinqToFutureValueFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void ToFutureValueWithSumOnEmptySetThrows()
5959
.Select(x => x.Id)
6060
.ToFutureValue(x => x.Sum());
6161

62-
Assert.That(() => personsSum.Value, Throws.InnerException.TypeOf<InvalidOperationException>().Or.InnerException.TypeOf<ArgumentNullException>());
62+
Assert.That(() => personsSum.Value, Throws.TypeOf<InvalidOperationException>().Or.InnerException.TypeOf<ArgumentNullException>());
6363
}
6464
}
6565

src/NHibernate.Test/Linq/ConstantTest.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ public void DmlPlansAreCached()
324324
}
325325

326326
[Test]
327-
public void PlansWithNonParameterizedConstantsAreNotCached()
327+
public void PlansWithNonParameterizedConstantsAreCached()
328328
{
329329
var queryPlanCacheType = typeof(QueryPlanCache);
330330

@@ -339,12 +339,12 @@ public void PlansWithNonParameterizedConstantsAreNotCached()
339339
select new { c.CustomerId, c.ContactName, Constant = 1 }).First();
340340
Assert.That(
341341
cache,
342-
Has.Count.EqualTo(0),
343-
"Query plan should not be cached.");
342+
Has.Count.EqualTo(1),
343+
"Query plan should be cached.");
344344
}
345345

346346
[Test]
347-
public void PlansWithNonParameterizedConstantsAreNotCachedForExpandedQuery()
347+
public void PlansWithNonParameterizedConstantsAreCachedForExpandedQuery()
348348
{
349349
var queryPlanCacheType = typeof(QueryPlanCache);
350350

@@ -360,8 +360,8 @@ public void PlansWithNonParameterizedConstantsAreNotCachedForExpandedQuery()
360360

361361
Assert.That(
362362
cache,
363-
Has.Count.EqualTo(0),
364-
"Query plan should not be cached.");
363+
Has.Count.EqualTo(2), // The second one is for the expanded expression that has two parameters
364+
"Query plan should be cached.");
365365
}
366366

367367
//GH-2298 - Different Update queries - same query cache plan

src/NHibernate.Test/Linq/ParameterTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ private void AssertTotalParameters<T>(IQueryable<T> query, int parameterNumber,
411411
{
412412
// In case of arrays linqParameterNumber and parameterNumber will be different
413413
Assert.That(
414-
GetLinqExpression(query).ParameterValuesByName.Count,
414+
GetLinqExpression(query).NamedParameters.Count,
415415
Is.EqualTo(linqParameterNumber ?? parameterNumber),
416416
"Linq expression has different number of parameters");
417417

0 commit comments

Comments
 (0)