Skip to content

Commit 3ec4b57

Browse files
Merge branch '5.2.x'
2 parents df6cbbe + 40779f9 commit 3ec4b57

File tree

12 files changed

+178
-11
lines changed

12 files changed

+178
-11
lines changed

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: 5.2.6.{build}
1+
version: 5.2.7.{build}
22
image: Visual Studio 2017
33
environment:
44
matrix:

build-common/NHibernate.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<PropertyGroup>
66
<VersionMajor Condition="'$(VersionMajor)' == ''">5</VersionMajor>
77
<VersionMinor Condition="'$(VersionMinor)' == ''">2</VersionMinor>
8-
<VersionPatch Condition="'$(VersionPatch)' == ''">6</VersionPatch>
8+
<VersionPatch Condition="'$(VersionPatch)' == ''">7</VersionPatch>
99
<VersionSuffix Condition="'$(VersionSuffix)' == ''"></VersionSuffix>
1010

1111
<VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>

build-common/common.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
<!-- This is used only for build folder -->
1515
<!-- TODO: Either remove or refactor to use NHibernate.props -->
16-
<property name="project.version" value="5.2.6" overwrite="false" />
17-
<property name="project.version.numeric" value="5.2.6" overwrite="false" />
16+
<property name="project.version" value="5.2.7" overwrite="false" />
17+
<property name="project.version.numeric" value="5.2.7" overwrite="false" />
1818

1919
<!-- properties used to connect to database for testing -->
2020
<include buildfile="nhibernate-properties.xml" />

releasenotes.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
Build 5.2.7
2+
=============================
3+
4+
Release notes - NHibernate - Version 5.2.7
5+
6+
4 issues were resolved in this release.
7+
8+
** Bug
9+
10+
* #2302 Backport sqlite.binaryguid to configuration schema
11+
* #2298 Dml Linq Update Produce Wrong Sql
12+
* #2296 Missing Row Count in Debug Log for Future queries
13+
14+
** Task
15+
16+
* #2303 Release 5.2.7
17+
118
Build 5.2.6
219
=============================
320

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Collections.Generic;
1212
using System.Linq;
1313
using System.Reflection;
14+
using NHibernate.Criterion;
1415
using NHibernate.DomainModel.Northwind.Entities;
1516
using NHibernate.Engine.Query;
1617
using NHibernate.Linq;
@@ -316,5 +317,62 @@ public async Task PlansWithNonParameterizedConstantsAreNotCachedAsync()
316317
Has.Count.EqualTo(0),
317318
"Query plan should not be cached.");
318319
}
320+
321+
[Test]
322+
public async Task PlansWithNonParameterizedConstantsAreNotCachedForExpandedQueryAsync()
323+
{
324+
var queryPlanCacheType = typeof(QueryPlanCache);
325+
326+
var cache = (SoftLimitMRUCache)
327+
queryPlanCacheType
328+
.GetField("planCache", BindingFlags.Instance | BindingFlags.NonPublic)
329+
.GetValue(Sfi.QueryPlanCache);
330+
cache.Clear();
331+
332+
var ids = new[] {"ANATR", "UNKNOWN"}.ToList();
333+
await (db.Customers.Where(x => ids.Contains(x.CustomerId)).Select(
334+
c => new {c.CustomerId, c.ContactName, Constant = 1}).FirstAsync());
335+
336+
Assert.That(
337+
cache,
338+
Has.Count.EqualTo(0),
339+
"Query plan should not be cached.");
340+
}
341+
342+
//GH-2298 - Different Update queries - same query cache plan
343+
[Test]
344+
public async Task DmlPlansForExpandedQueryAsync()
345+
{
346+
var queryPlanCacheType = typeof(QueryPlanCache);
347+
348+
var cache = (SoftLimitMRUCache)
349+
queryPlanCacheType
350+
.GetField("planCache", BindingFlags.Instance | BindingFlags.NonPublic)
351+
.GetValue(Sfi.QueryPlanCache);
352+
cache.Clear();
353+
354+
using (session.BeginTransaction())
355+
{
356+
var list = new[] {"UNKNOWN", "UNKNOWN2"}.ToList();
357+
await (db.Customers.Where(x => list.Contains(x.CustomerId)).UpdateAsync(
358+
x => new Customer
359+
{
360+
CompanyName = "Constant1"
361+
}));
362+
363+
await (db.Customers.Where(x => list.Contains(x.CustomerId))
364+
.UpdateAsync(
365+
x => new Customer
366+
{
367+
ContactName = "Constant1"
368+
}));
369+
370+
Assert.That(
371+
cache.Count,
372+
//2 original queries + 2 expanded queries are expected in cache
373+
Is.EqualTo(0).Or.EqualTo(4),
374+
"Query plans should either be cached separately or not cached at all.");
375+
}
376+
}
319377
}
320378
}

src/NHibernate.Test/Linq/ConstantTest.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Generic;
22
using System.Linq;
33
using System.Reflection;
4+
using NHibernate.Criterion;
45
using NHibernate.DomainModel.Northwind.Entities;
56
using NHibernate.Engine.Query;
67
using NHibernate.Linq;
@@ -337,5 +338,62 @@ public void PlansWithNonParameterizedConstantsAreNotCached()
337338
Has.Count.EqualTo(0),
338339
"Query plan should not be cached.");
339340
}
341+
342+
[Test]
343+
public void PlansWithNonParameterizedConstantsAreNotCachedForExpandedQuery()
344+
{
345+
var queryPlanCacheType = typeof(QueryPlanCache);
346+
347+
var cache = (SoftLimitMRUCache)
348+
queryPlanCacheType
349+
.GetField("planCache", BindingFlags.Instance | BindingFlags.NonPublic)
350+
.GetValue(Sfi.QueryPlanCache);
351+
cache.Clear();
352+
353+
var ids = new[] {"ANATR", "UNKNOWN"}.ToList();
354+
db.Customers.Where(x => ids.Contains(x.CustomerId)).Select(
355+
c => new {c.CustomerId, c.ContactName, Constant = 1}).First();
356+
357+
Assert.That(
358+
cache,
359+
Has.Count.EqualTo(0),
360+
"Query plan should not be cached.");
361+
}
362+
363+
//GH-2298 - Different Update queries - same query cache plan
364+
[Test]
365+
public void DmlPlansForExpandedQuery()
366+
{
367+
var queryPlanCacheType = typeof(QueryPlanCache);
368+
369+
var cache = (SoftLimitMRUCache)
370+
queryPlanCacheType
371+
.GetField("planCache", BindingFlags.Instance | BindingFlags.NonPublic)
372+
.GetValue(Sfi.QueryPlanCache);
373+
cache.Clear();
374+
375+
using (session.BeginTransaction())
376+
{
377+
var list = new[] {"UNKNOWN", "UNKNOWN2"}.ToList();
378+
db.Customers.Where(x => list.Contains(x.CustomerId)).Update(
379+
x => new Customer
380+
{
381+
CompanyName = "Constant1"
382+
});
383+
384+
db.Customers.Where(x => list.Contains(x.CustomerId))
385+
.Update(
386+
x => new Customer
387+
{
388+
ContactName = "Constant1"
389+
});
390+
391+
Assert.That(
392+
cache.Count,
393+
//2 original queries + 2 expanded queries are expected in cache
394+
Is.EqualTo(0).Or.EqualTo(4),
395+
"Query plans should either be cached separately or not cached at all.");
396+
}
397+
}
340398
}
341399
}

src/NHibernate/Async/Multi/QueryBatchItemBase.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public async Task<int> ProcessResultsSetAsync(DbDataReader reader, CancellationT
3434

3535
var dialect = Session.Factory.Dialect;
3636
var hydratedObjects = new List<object>[_queryInfos.Count];
37+
var isDebugLog = Log.IsDebugEnabled();
3738

3839
using (Session.SwitchCacheMode(_cacheMode))
3940
{
@@ -81,8 +82,15 @@ public async Task<int> ProcessResultsSetAsync(DbDataReader reader, CancellationT
8182
if (ownCacheBatcher)
8283
cacheBatcher = new CacheBatcher(Session);
8384

84-
for (var count = 0; count < maxRows && await (reader.ReadAsync(cancellationToken)).ConfigureAwait(false); count++)
85+
if (isDebugLog)
86+
Log.Debug("processing result set");
87+
88+
int count;
89+
for (count = 0; count < maxRows && await (reader.ReadAsync(cancellationToken)).ConfigureAwait(false); count++)
8590
{
91+
if (isDebugLog)
92+
Log.Debug("result set row: {0}", count);
93+
8694
rowCount++;
8795

8896
var o =
@@ -108,6 +116,9 @@ public async Task<int> ProcessResultsSetAsync(DbDataReader reader, CancellationT
108116
tmpResults.Add(o);
109117
}
110118

119+
if (isDebugLog)
120+
Log.Debug("done processing result set ({0} rows)", count);
121+
111122
queryInfo.Result = tmpResults;
112123
if (queryInfo.CanPutToCache)
113124
queryInfo.ResultToCache = queryCacheBuilder.Result;

src/NHibernate/Engine/Query/QueryPlanCache.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public IQueryExpressionPlan GetHQLQueryPlan(IQueryExpression queryExpression, bo
6262
}
6363
plan = new QueryExpressionPlan(queryExpression, shallow, enabledFilters, factory);
6464
// 6.0 TODO: add "CanCachePlan { get; }" to IQueryExpression interface
65-
if (!(queryExpression is NhLinqExpression linqExpression) || linqExpression.CanCachePlan)
65+
if (!(queryExpression is ICacheableQueryExpression linqExpression) || linqExpression.CanCachePlan)
6666
planCache.Put(key, plan);
6767
else
6868
log.Debug("Query plan not cacheable");
@@ -117,7 +117,7 @@ public IQueryExpressionPlan GetFilterQueryPlan(IQueryExpression queryExpression,
117117
log.Debug("unable to locate collection-filter query plan in cache; generating ({0} : {1})", collectionRole, queryExpression.Key);
118118
plan = new FilterQueryPlan(queryExpression, collectionRole, shallow, enabledFilters, factory);
119119
// 6.0 TODO: add "CanCachePlan { get; }" to IQueryExpression interface
120-
if (!(queryExpression is NhLinqExpression linqExpression) || linqExpression.CanCachePlan)
120+
if (!(queryExpression is ICacheableQueryExpression linqExpression) || linqExpression.CanCachePlan)
121121
planCache.Put(key, plan);
122122
else
123123
log.Debug("Query plan not cacheable");

src/NHibernate/IQueryExpression.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@
55

66
namespace NHibernate
77
{
8+
//TODO 6.0: Merge into IQueryExpression
9+
internal interface ICacheableQueryExpression
10+
{
11+
bool CanCachePlan { get; }
12+
}
13+
814
public interface IQueryExpression
915
{
1016
IASTNode Translate(ISessionFactoryImplementor sessionFactory, bool filter);
1117
string Key { get; }
1218
System.Type Type { get; }
1319
IList<NamedParameterDescriptor> ParameterDescriptors { get; }
1420
}
15-
}
21+
}

src/NHibernate/Impl/ExpressionQueryImpl.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,16 +192,18 @@ public override object[] ValueArray()
192192
}
193193
}
194194

195-
internal class ExpandedQueryExpression : IQueryExpression
195+
internal class ExpandedQueryExpression : IQueryExpression, ICacheableQueryExpression
196196
{
197197
private readonly IASTNode _tree;
198+
private ICacheableQueryExpression _cacheableExpression;
198199

199200
public ExpandedQueryExpression(IQueryExpression queryExpression, IASTNode tree, string key)
200201
{
201202
_tree = tree;
202203
Key = key;
203204
Type = queryExpression.Type;
204205
ParameterDescriptors = queryExpression.ParameterDescriptors;
206+
_cacheableExpression = queryExpression as ICacheableQueryExpression;
205207
}
206208

207209
#region IQueryExpression Members
@@ -218,6 +220,8 @@ public IASTNode Translate(ISessionFactoryImplementor sessionFactory, bool filter
218220
public IList<NamedParameterDescriptor> ParameterDescriptors { get; private set; }
219221

220222
#endregion
223+
224+
public bool CanCachePlan => _cacheableExpression?.CanCachePlan ?? true;
221225
}
222226

223227
internal class ParameterExpander

src/NHibernate/Linq/NhLinqExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace NHibernate.Linq
1313
{
14-
public class NhLinqExpression : IQueryExpression
14+
public class NhLinqExpression : IQueryExpression, ICacheableQueryExpression
1515
{
1616
public string Key { get; protected set; }
1717

src/NHibernate/Multi/QueryBatchItemBase.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace NHibernate.Multi
1616
/// </summary>
1717
public abstract partial class QueryBatchItemBase<TResult> : IQueryBatchItem<TResult>, IQueryBatchItemWithAsyncProcessResults
1818
{
19+
private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof(QueryBatch));
20+
1921
protected ISessionImplementor Session;
2022
private List<EntityKey[]>[] _subselectResultKeys;
2123
private List<QueryInfo> _queryInfos;
@@ -178,6 +180,7 @@ public int ProcessResultsSet(DbDataReader reader)
178180

179181
var dialect = Session.Factory.Dialect;
180182
var hydratedObjects = new List<object>[_queryInfos.Count];
183+
var isDebugLog = Log.IsDebugEnabled();
181184

182185
using (Session.SwitchCacheMode(_cacheMode))
183186
{
@@ -225,8 +228,15 @@ public int ProcessResultsSet(DbDataReader reader)
225228
if (ownCacheBatcher)
226229
cacheBatcher = new CacheBatcher(Session);
227230

228-
for (var count = 0; count < maxRows && reader.Read(); count++)
231+
if (isDebugLog)
232+
Log.Debug("processing result set");
233+
234+
int count;
235+
for (count = 0; count < maxRows && reader.Read(); count++)
229236
{
237+
if (isDebugLog)
238+
Log.Debug("result set row: {0}", count);
239+
230240
rowCount++;
231241

232242
var o =
@@ -252,6 +262,9 @@ public int ProcessResultsSet(DbDataReader reader)
252262
tmpResults.Add(o);
253263
}
254264

265+
if (isDebugLog)
266+
Log.Debug("done processing result set ({0} rows)", count);
267+
255268
queryInfo.Result = tmpResults;
256269
if (queryInfo.CanPutToCache)
257270
queryInfo.ResultToCache = queryCacheBuilder.Result;

0 commit comments

Comments
 (0)