Skip to content

Commit 2c9cc58

Browse files
committed
Support non parameterized constants query plan caching for Linq provider
1 parent ac5e116 commit 2c9cc58

File tree

79 files changed

+1618
-221
lines changed

Some content is hidden

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

79 files changed

+1618
-221
lines changed

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
@@ -322,7 +322,7 @@ public async Task UsingTwoParametersInDMLDeleteAsync()
322322
{
323323
// In case of arrays linqParameterNumber and parameterNumber will be different
324324
Assert.That(
325-
GetLinqExpression(query).ParameterValuesByName.Count,
325+
GetLinqExpression(query).NamedParameters.Count,
326326
Is.EqualTo(linqParameterNumber ?? parameterNumber),
327327
"Linq expression has different number of parameters");
328328

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/SecondLevelCacheTest/QueryCacheFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,4 +327,4 @@ public IList TransformList(IList collection)
327327
}
328328
}
329329
}
330-
}
330+
}

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
@@ -383,7 +383,7 @@ private void AssertTotalParameters<T>(IQueryable<T> query, int parameterNumber,
383383
{
384384
// In case of arrays linqParameterNumber and parameterNumber will be different
385385
Assert.That(
386-
GetLinqExpression(query).ParameterValuesByName.Count,
386+
GetLinqExpression(query).NamedParameters.Count,
387387
Is.EqualTo(linqParameterNumber ?? parameterNumber),
388388
"Linq expression has different number of parameters");
389389

src/NHibernate.Test/Linq/ParameterisedQueries.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ public void Expressions_Differing_Only_By_Constants_Return_The_Same_Key()
4949
var nhNewYork = new NhLinqExpression(newYork.Body, Sfi);
5050

5151
Assert.AreEqual(nhLondon.Key, nhNewYork.Key);
52-
Assert.AreEqual(1, nhLondon.ParameterValuesByName.Count);
53-
Assert.AreEqual(1, nhNewYork.ParameterValuesByName.Count);
54-
Assert.AreEqual("London", nhLondon.ParameterValuesByName.First().Value.Item1);
55-
Assert.AreEqual("New York", nhNewYork.ParameterValuesByName.First().Value.Item1);
52+
Assert.AreEqual(1, nhLondon.NamedParameters.Count);
53+
Assert.AreEqual(1, nhNewYork.NamedParameters.Count);
54+
Assert.AreEqual("London", nhLondon.NamedParameters.First().Value.Value);
55+
Assert.AreEqual("New York", nhNewYork.NamedParameters.First().Value.Value);
5656
}
5757
}
5858

@@ -72,13 +72,13 @@ public void CanSpecifyParameterTypeInfo()
7272
var nhLondon = new NhLinqExpression(london.Body, Sfi);
7373
var nhNewYork = new NhLinqExpression(newYork.Body, Sfi);
7474

75-
var londonParameter = nhLondon.ParameterValuesByName.Single().Value;
76-
Assert.That(londonParameter.Item1, Is.EqualTo("London"));
77-
Assert.That(londonParameter.Item2, Is.EqualTo(NHibernateUtil.StringClob));
75+
var londonParameter = nhLondon.NamedParameters.Single().Value;
76+
Assert.That(londonParameter.Value, Is.EqualTo("London"));
77+
Assert.That(londonParameter.Type, Is.EqualTo(NHibernateUtil.StringClob));
7878

79-
var newYorkParameter = nhNewYork.ParameterValuesByName.Single().Value;
80-
Assert.That(newYorkParameter.Item1, Is.EqualTo("New York"));
81-
Assert.That(newYorkParameter.Item2, Is.EqualTo(NHibernateUtil.AnsiString));
79+
var newYorkParameter = nhNewYork.NamedParameters.Single().Value;
80+
Assert.That(newYorkParameter.Value, Is.EqualTo("New York"));
81+
Assert.That(newYorkParameter.Type, Is.EqualTo(NHibernateUtil.AnsiString));
8282
}
8383
}
8484

@@ -239,4 +239,4 @@ protected override string[] Mappings
239239
get { return Array.Empty<string>(); }
240240
}
241241
}
242-
}
242+
}

0 commit comments

Comments
 (0)