Skip to content

Conversation

fredericDelaporte
Copy link
Member

@fredericDelaporte fredericDelaporte commented Mar 13, 2018

TypedValue may use the default comparer instead of the list comparer when a list parameter is supplied with a HashSet. This will then result in undue cache miss.

This PR is build over #1611 (two first commit currently appearing here).

}

[Test]
public void Equality()
{
// Equality is aware only by parameters names not values
Copy link
Member Author

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);
Copy link
Member Author

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);
Copy link
Member Author

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);
Copy link
Member Author

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)
Copy link
Member Author

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);
Copy link
Member Author

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.

@hazzik
Copy link
Member

hazzik commented May 10, 2018

Needs to be rewritten as 5.1.2 is merged to master now.

@fredericDelaporte fredericDelaporte force-pushed the TypedValue branch 2 times, most recently from 608a925 to abed562 Compare May 10, 2018 12:10
@fredericDelaporte
Copy link
Member Author

Done.

@fredericDelaporte
Copy link
Member Author

If this PR is merged after #1613, it should then further adjusts query and filter key hashcode tests.

@@ -13,6 +13,8 @@ public class FilterKey
private readonly string _filterName;
private readonly Dictionary<string, TypedValue> _filterParameters = new Dictionary<string, TypedValue>();

// Since v5.1
Copy link
Member

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();
Copy link
Member

@hazzik hazzik May 10, 2018

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;
}

Copy link
Member Author

@fredericDelaporte fredericDelaporte May 11, 2018

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.)

@fredericDelaporte
Copy link
Member Author

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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this...

Copy link
Member Author

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.

@@ -66,50 +81,60 @@ public bool Equals(TypedValue x, TypedValue y)
if (y == null) return false;
Copy link
Member

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?

Copy link
Member

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

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));
Copy link
Member Author

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;
Copy link
Member Author

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;
Copy link
Member Author

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.

@fredericDelaporte fredericDelaporte merged commit 0d61f46 into nhibernate:master May 28, 2018
@fredericDelaporte fredericDelaporte deleted the TypedValue branch May 28, 2018 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants