Skip to content

Commit cb0b7ce

Browse files
committed
Support non parameterized constants query plan caching for Linq provider
1 parent 6b441a9 commit cb0b7ce

File tree

80 files changed

+1655
-226
lines changed

Some content is hidden

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

80 files changed

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

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)