Skip to content

Fix IsModified so that a null equates empty components when using select-before-update #1487

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

Merged
merged 3 commits into from
Dec 16, 2017

Conversation

druidroad
Copy link
Contributor

Test case and proposed solution for (#1486)

…Unable to cast…).

Test case and proposed solution for nhibernate#1486
@@ -534,7 +534,7 @@ public override bool IsModified(object old, object current, bool[] checkable, IS
return current != null;
}
object[] currentValues = GetPropertyValues(current, session);
object[] oldValues = (Object[]) old;
object[] oldValues = old is object[] ? (Object[]) old : GetPropertyValues(old, session);
Copy link
Member

Choose a reason for hiding this comment

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

This is a double cast implementation, which should be avoided. And better apply the fix from Hibernate as pointed by @hazzik. (hibernate/hibernate-orm@550c2e3).

Granted, this will fix another issue by the way: bad handling of empty components. (They should be considered equal to null, which current implementation (with or without your fix) does not handle.) So their own test (hibernate/hibernate-orm@667136c) should be imported, and this PR/issue title and description should be adjusted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the comments. Will do.

I adjusted Hibernate HHH-11237 test case.  Hibernate runs the test for CREATE_EMPTY_COMPOSITES_ENABLED true and false that sets createEmptyCompositesEnabled for ComponentType. We don’t have that property/case in NHibernate.
I had to change ComponentType.GetPropertyValue(object component, int i) to handle nulls. I applied the same code from Hibernate.
@druidroad druidroad changed the title #1486 ComponentType.IsModified(…) method throws exception (Unable to cast...) #1486 Fix IsModified so that a null equates empty components when using select-before-update Dec 15, 2017
@druidroad
Copy link
Contributor Author

I committed new updated test case and fix.

Copy link
Member

@fredericDelaporte fredericDelaporte left a comment

Choose a reason for hiding this comment

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

I have added a commit for addressing the points I have reviewed.

if (component is Object[])
{
return ((Object[]) component)[i];
}
Copy link
Member

@fredericDelaporte fredericDelaporte Dec 15, 2017

Choose a reason for hiding this comment

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

This object[] type test is a thing from yet another Hibernate change, hibernate/hibernate-orm@ac16473 for HHH-9003 about reducing array allocations. Ideally it should no be taken here. But it mixes with the way they have fixed IsModified too, so it also implies some adjustments on IsModified too.

By the way, when porting Java code, we do some c# specific changes, like respecting c# styling (object rather than Object, namings, ...), exploiting c# features (pattern matching usable here for avoiding double cast), ...
By example this part could be written:

switch (component)
{
	case null:
		return null;
	case object[] componentArray:
		return componentArray[i];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I will respect c# styling.

@@ -269,6 +269,14 @@ public override object NullSafeGet(DbDataReader rs, string name, ISessionImpleme

public object GetPropertyValue(object component, int i)
{
if (component == null)
{
Copy link
Member

@fredericDelaporte fredericDelaporte Dec 15, 2017

Choose a reason for hiding this comment

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

Well, so the added test case demonstrates this part was lacking. In fact we are in a somewhat special situation here.
Treating a null component as equivalent to a component having all its properties null was added in NHibernate with NH-1101, which has done most of the changes in the tuplizer, not in the component type. Doing the change in component tuplizer GetPropertyValue was forgotten.

On Hibernate side, it has been done four years later with HHH-7610, with a different implementation, doing the changes mostly in component type instead of doing it in tuplizers.

For consistency with NH-1101, I think we should do the fix here in the tuplizer, instead of doing it in the component type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it makes sense to fix in tuplizer.

{
int len = propertyTypes[i].GetColumnSpan(session.Factory);
bool[] subcheckable = new bool[len];
Array.Copy(checkable, loc, subcheckable, 0, len);
if (propertyTypes[i].IsModified(oldValues[i], currentValues[i], subcheckable, session))
if (propertyTypes[i].IsModified(GetPropertyValue(old, i), GetPropertyValue(current, i), subcheckable, session))
Copy link
Member

@fredericDelaporte fredericDelaporte Dec 15, 2017

Choose a reason for hiding this comment

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

This takes a part of HHH-9003 change too. I think we would have better going back to your initial change for this part, due to the conflicting changes mix between NH-1101, HHH-7610, HHH-9003 and HHH-11237 , sorry.

(By the way the HHH-9003 change here does not look very efficient, along with the change of the other method it causes a cast in the loop. Your change is really better: var oldValues = old is object[] objects ? objects : GetPropertyValues(old, session);.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it makes sense.

interceptor.Reset();
john.Address = null;
session.Flush();
Assert.AreEqual(0, interceptor.CallCount);
Copy link
Member

Choose a reason for hiding this comment

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

We favor using the fluent assert model, as the model used here is the "old model" and lacks features. See two models.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I will use "fluent assert model" in my code.

transaction.Commit();
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

You own test case was testing another currently failing case, I think it is worth keeping it too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok

@fredericDelaporte fredericDelaporte self-assigned this Dec 15, 2017
Copy link
Contributor Author

@druidroad druidroad left a comment

Choose a reason for hiding this comment

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

These code changes make sense. Thank you for the detail comments and tips how to proceed with NHibernate coding, patterns and issue investigation. Its my 1st contribution to NHibernate and sorry if I took a lot of your time for code review. Is there anything else I should do to complete this PR?

@fredericDelaporte
Copy link
Member

fredericDelaporte commented Dec 15, 2017

No troubles, you have been helpful, no reason to be sorry. In fact it is more me who should, because "taking the fix from Hibernate" turned out as not a good idea for this case. Hibernate was indeed fixing your initial report only by accident, and taking its fix had cause fixing quite something else. So in effect we hijacked your issue&PR for fixing something else (fortunately while still fixing your initial report). That is not really a recommended practice.

Is there anything else I should do to complete this PR?

No, I intend to wait for end of tests, then squash & merge.

@fredericDelaporte fredericDelaporte merged commit 7e440bf into nhibernate:master Dec 16, 2017
@fredericDelaporte fredericDelaporte added this to the 5.1 milestone Dec 16, 2017
fredericDelaporte added a commit to fredericDelaporte/nhibernate-core that referenced this pull request Dec 16, 2017
fredericDelaporte added a commit that referenced this pull request Dec 16, 2017
@hazzik hazzik changed the title #1486 Fix IsModified so that a null equates empty components when using select-before-update Fix IsModified so that a null equates empty components when using select-before-update Dec 29, 2017
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