Skip to content

Commit dae54d5

Browse files
Merge pull request #2305 from fredericDelaporte/Backport2298
Backport Dml Linq Update Produce Wrong Sql Release 5.1.7
2 parents edadc1e + fbeb7a5 commit dae54d5

File tree

10 files changed

+109
-10
lines changed

10 files changed

+109
-10
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.1.6.{build}
1+
version: 5.1.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
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<VersionMajor Condition="'$(VersionMajor)' == ''">5</VersionMajor>
44
<VersionMinor Condition="'$(VersionMinor)' == ''">1</VersionMinor>
5-
<VersionPatch Condition="'$(VersionPatch)' == ''">6</VersionPatch>
5+
<VersionPatch Condition="'$(VersionPatch)' == ''">7</VersionPatch>
66
<VersionSuffix Condition="'$(VersionSuffix)' == ''"></VersionSuffix>
77

88
<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.1.6" overwrite="false" />
17-
<property name="project.version.numeric" value="5.1.6" overwrite="false" />
16+
<property name="project.version" value="5.1.7" overwrite="false" />
17+
<property name="project.version.numeric" value="5.1.7" overwrite="false" />
1818

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

releasenotes.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
Build 5.1.7
2+
=============================
3+
4+
Release notes - NHibernate - Version 5.1.7
5+
6+
** Bug
7+
* #2298 Dml Linq Update Produce Wrong Sql
8+
19
Build 5.1.6
210
=============================
311

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
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;
17+
using NHibernate.Linq;
1618
using NHibernate.Linq.Visitors;
1719
using NHibernate.Util;
1820
using NUnit.Framework;
19-
using NHibernate.Linq;
2021

2122
namespace NHibernate.Test.Linq
2223
{
@@ -269,5 +270,26 @@ public async Task PlansWithNonParameterizedConstantsAreNotCachedAsync()
269270
Has.Count.EqualTo(0),
270271
"Query plan should not be cached.");
271272
}
273+
274+
[Test]
275+
public async Task PlansWithNonParameterizedConstantsAreNotCachedForExpandedQueryAsync()
276+
{
277+
var queryPlanCacheType = typeof(QueryPlanCache);
278+
279+
var cache = (SoftLimitMRUCache)
280+
queryPlanCacheType
281+
.GetField("planCache", BindingFlags.Instance | BindingFlags.NonPublic)
282+
.GetValue(Sfi.QueryPlanCache);
283+
cache.Clear();
284+
285+
var ids = new[] {"ANATR", "UNKNOWN"}.ToList();
286+
await (db.Customers.Where(x => ids.Contains(x.CustomerId)).Select(
287+
c => new {c.CustomerId, c.ContactName, Constant = 1}).FirstAsync());
288+
289+
Assert.That(
290+
cache,
291+
Has.Count.EqualTo(0),
292+
"Query plan should not be cached.");
293+
}
272294
}
273295
}

src/NHibernate.Test/Linq/ConstantTest.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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;
7+
using NHibernate.Linq;
68
using NHibernate.Linq.Visitors;
79
using NHibernate.Util;
810
using NUnit.Framework;
@@ -276,5 +278,62 @@ public void PlansWithNonParameterizedConstantsAreNotCached()
276278
Has.Count.EqualTo(0),
277279
"Query plan should not be cached.");
278280
}
281+
282+
[Test]
283+
public void PlansWithNonParameterizedConstantsAreNotCachedForExpandedQuery()
284+
{
285+
var queryPlanCacheType = typeof(QueryPlanCache);
286+
287+
var cache = (SoftLimitMRUCache)
288+
queryPlanCacheType
289+
.GetField("planCache", BindingFlags.Instance | BindingFlags.NonPublic)
290+
.GetValue(Sfi.QueryPlanCache);
291+
cache.Clear();
292+
293+
var ids = new[] {"ANATR", "UNKNOWN"}.ToList();
294+
db.Customers.Where(x => ids.Contains(x.CustomerId)).Select(
295+
c => new {c.CustomerId, c.ContactName, Constant = 1}).First();
296+
297+
Assert.That(
298+
cache,
299+
Has.Count.EqualTo(0),
300+
"Query plan should not be cached.");
301+
}
302+
303+
//GH-2298 - Different Update queries - same query cache plan
304+
[Test]
305+
public void DmlPlansForExpandedQuery()
306+
{
307+
var queryPlanCacheType = typeof(QueryPlanCache);
308+
309+
var cache = (SoftLimitMRUCache)
310+
queryPlanCacheType
311+
.GetField("planCache", BindingFlags.Instance | BindingFlags.NonPublic)
312+
.GetValue(Sfi.QueryPlanCache);
313+
cache.Clear();
314+
315+
using (session.BeginTransaction())
316+
{
317+
var list = new[] {"UNKNOWN", "UNKNOWN2"}.ToList();
318+
db.Customers.Where(x => list.Contains(x.CustomerId)).Update(
319+
x => new Customer
320+
{
321+
CompanyName = "Constant1"
322+
});
323+
324+
db.Customers.Where(x => list.Contains(x.CustomerId))
325+
.Update(
326+
x => new Customer
327+
{
328+
ContactName = "Constant1"
329+
});
330+
331+
Assert.That(
332+
cache.Count,
333+
//2 original queries + 2 expanded queries are expected in cache
334+
Is.EqualTo(0).Or.EqualTo(4),
335+
"Query plans should either be cached separately or not cached at all.");
336+
}
337+
}
279338
}
280339
}

src/NHibernate/Engine/Query/QueryPlanCache.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public IQueryExpressionPlan GetHQLQueryPlan(IQueryExpression queryExpression, bo
6060
}
6161
plan = new QueryExpressionPlan(queryExpression, shallow, enabledFilters, factory);
6262
// 6.0 TODO: add "CanCachePlan { get; }" to IQueryExpression interface
63-
if (!(queryExpression is NhLinqExpression linqExpression) || linqExpression.CanCachePlan)
63+
if (!(queryExpression is ICacheableQueryExpression linqExpression) || linqExpression.CanCachePlan)
6464
planCache.Put(key, plan);
6565
else
6666
log.Debug("Query plan not cacheable");
@@ -115,7 +115,7 @@ public IQueryExpressionPlan GetFilterQueryPlan(IQueryExpression queryExpression,
115115
log.Debug("unable to locate collection-filter query plan in cache; generating ({0} : {1})", collectionRole, queryExpression.Key);
116116
plan = new FilterQueryPlan(queryExpression, collectionRole, shallow, enabledFilters, factory);
117117
// 6.0 TODO: add "CanCachePlan { get; }" to IQueryExpression interface
118-
if (!(queryExpression is NhLinqExpression linqExpression) || linqExpression.CanCachePlan)
118+
if (!(queryExpression is ICacheableQueryExpression linqExpression) || linqExpression.CanCachePlan)
119119
planCache.Put(key, plan);
120120
else
121121
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
@@ -150,16 +150,18 @@ public override object[] ValueArray()
150150
}
151151
}
152152

153-
internal class ExpandedQueryExpression : IQueryExpression
153+
internal class ExpandedQueryExpression : IQueryExpression, ICacheableQueryExpression
154154
{
155155
private readonly IASTNode _tree;
156+
private ICacheableQueryExpression _cacheableExpression;
156157

157158
public ExpandedQueryExpression(IQueryExpression queryExpression, IASTNode tree, string key)
158159
{
159160
_tree = tree;
160161
Key = key;
161162
Type = queryExpression.Type;
162163
ParameterDescriptors = queryExpression.ParameterDescriptors;
164+
_cacheableExpression = queryExpression as ICacheableQueryExpression;
163165
}
164166

165167
#region IQueryExpression Members
@@ -176,6 +178,8 @@ public IASTNode Translate(ISessionFactoryImplementor sessionFactory, bool filter
176178
public IList<NamedParameterDescriptor> ParameterDescriptors { get; private set; }
177179

178180
#endregion
181+
182+
public bool CanCachePlan => _cacheableExpression?.CanCachePlan ?? true;
179183
}
180184

181185
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

0 commit comments

Comments
 (0)