-
Notifications
You must be signed in to change notification settings - Fork 933
Fix TypedValue not always using adequate comparer with SetParameterList #1612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix TypedValue not always using adequate comparer with SetParameterList #1612
Conversation
5b46348
to
da7b6d4
Compare
} | ||
|
||
[Test] | ||
public void Equality() | ||
{ | ||
// Equality is aware only by parameters names not values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong, but not contradicted by the test because the test was bugged.
|
||
var f1 = new FilterImpl(Sfi.GetFilterDefinition(filterName)); | ||
f1.SetParameter("pLike", "%ing"); | ||
fk1 = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here lies the test bug, affecting all tests: the second key was construct with the first filter values...
|
||
var f1 = new FilterImpl(Sfi.GetFilterDefinition(filterName)); | ||
f1.SetParameter("pDesc", "something").SetParameter("pValue", 11); | ||
fk1 = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same test bug than in FilterDescLikeToCompare
, fixed too.
FilterKey fk, fk1; | ||
FilterDescLikeToCompare(out fk, out fk1); | ||
Assert.That(fk, Is.EqualTo(fk1)); | ||
FilterDescLikeToCompare(out var fk, out var fk1, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They can be equal only with same value. The NotEquality
test is completed with an assert showcasing that a different value causes inequality.
} | ||
|
||
[Test] | ||
public void HashCode() | ||
{ | ||
// HashCode is aware only by parameters names not values (should work as Equal) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately true for the current hashcode implementation, which is bad for performances. But in fact it should ideally not be equal, since actually filter with different parameters values are not equals.
f1.SetParameter("pLike", "%ing"); | ||
var fk1 = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes); | ||
f1.SetParameter("pLike", sameValue ? "so%" : "%ing"); | ||
var fk1 = new FilterKey(f1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same test bug as in FilterKeyFixture
.
1644337
to
c7894b0
Compare
Needs to be rewritten as 5.1.2 is merged to master now. |
608a925
to
abed562
Compare
Done. |
If this PR is merged after #1613, it should then further adjusts query and filter key hashcode tests. |
abed562
to
cd0214e
Compare
src/NHibernate/Cache/FilterKey.cs
Outdated
@@ -13,6 +13,8 @@ public class FilterKey | |||
private readonly string _filterName; | |||
private readonly Dictionary<string, TypedValue> _filterParameters = new Dictionary<string, TypedValue>(); | |||
|
|||
// Since v5.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
5.2
{ | ||
if (x == y) | ||
return true; | ||
|
||
if (x == null || y == null) | ||
return false; | ||
|
||
if (x.Count != y.Count) | ||
if (x is ICollection xCol && y is ICollection yCol && xCol.Count != yCol.Count) | ||
return false; | ||
|
||
var ye = y.GetEnumerator(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are a few bugs:
[Test]
public void PaddedShouldNotEqualShort()
{
var v1 = new TypedValue(NHibernateUtil.Int32, ToLazyEnumerable(3, 2, 1, 0, 0, 0), true);
var v2 = new TypedValue(NHibernateUtil.Int32, ToLazyEnumerable(3, 2, 1, 0), true);
Warn.If(v1.GetHashCode() == v2.GetHashCode(), "Equal hash codes");
Assert.AreNotEqual(v1, v2);
}
[Test]
public void ShortShouldNotEqualLonger()
{
var v1 = new TypedValue(NHibernateUtil.Int32, ToLazyEnumerable(1, 2, 3), true);
var v2 = new TypedValue(NHibernateUtil.Int32, ToLazyEnumerable(1, 2, 3, 1, -1), true);
Warn.If(v1.GetHashCode() == v2.GetHashCode(), "Equal hash codes");
Assert.AreNotEqual(v1, v2);
}
static IEnumerable<T> ToLazyEnumerable<T>(params T[] array)
{
// We want to get an IEnumerable which is lazy (eg does not implement ICollection interface)
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var item in array) yield return item;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, my bad. (Now fixed.)
f9b2257
to
315c79c
Compare
Rebased and hashcode tests enabled. (All commits are to be squashed in one.) |
} | ||
} | ||
|
||
return list; | ||
} | ||
else | ||
{ | ||
return _values.Select(v => new TypedValue(type, v)).ToList(); | ||
return _values.Select(v => new TypedValue(type, v, false)).ToList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_values
is the collection of values to be put in the in
clause. They must be elements, not themselves lists.
src/NHibernate/Engine/TypedValue.cs
Outdated
@@ -66,50 +81,60 @@ public bool Equals(TypedValue x, TypedValue y) | |||
if (y == null) return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add reference equals short-cut?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And for DefaultComparer as well
660a35c
to
bfafb14
Compare
And add an argument pre-condition check. To be squashed.
public TypedValue(IType type, object value, bool isList) | ||
{ | ||
if (isList && value != null && !(value is IEnumerable)) | ||
throw new ArgumentException($"{nameof(value)} must be an {nameof(IEnumerable)} when {nameof(isList)} is true", nameof(value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case some mistake is done later, better check value
is at least IEnumerable
when isList
is true. (The list comparator will consider as always equals values which are both not IEnumerable
.)
@@ -63,53 +80,66 @@ public class ParameterListComparer : IEqualityComparer<TypedValue> | |||
{ | |||
public bool Equals(TypedValue x, TypedValue y) | |||
{ | |||
if (y == null) return false; | |||
if (ReferenceEquals(x, y)) | |||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shortcut added, although I do not expect it to be much useful, since its main usage is in query cache key comparison. Unless a not originally cached query instance is re-executed a second time (which is likely a code smell), typed values will be different instances.
if (ReferenceEquals(x, y)) | ||
return true; | ||
if (x == null || y == null) | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With its current usage, x
is never supposed to be null
. But as the comparer class is publicly accessible, better still check this.
TypedValue
may use the default comparer instead of the list comparer when a list parameter is supplied with aHashSet
. This will then result in undue cache miss.This PR is build over #1611 (two first commit currently appearing here).