Skip to content

Fix StackOverflowException in DefaultDynamicLazyFieldInterceptor #1437

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 2 commits into from
Nov 19, 2017
Merged

Conversation

offan
Copy link
Contributor

@offan offan commented Nov 16, 2017

Fixes #1436

@offan
Copy link
Contributor Author

offan commented Nov 16, 2017

fixed the test

@@ -50,7 +50,7 @@ protected override void OnTearDown()
using (var s = OpenSession())
using (var tx = s.BeginTransaction())
{
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(1));
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(2));
Copy link
Member

Choose a reason for hiding this comment

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

Since I have commented on the commit (no PR at that time), the PR was not showing the line I was commenting. This was this one.

I consider it should be changed to:

s.CreateQuery("delete from Book").ExecuteUpdate();

There are no reason to assert the number of inserted things in the teardown. So better remove this undue old assert rather than fixing it. (By the way, it is better to use a HQL delete rather than a SQL one, so I have changed that too in the above code.)


[Test]
public void CanMergeWithLazyProperty()
{
Copy link
Member

Choose a reason for hiding this comment

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

Formatting glitch.

@offan
Copy link
Contributor Author

offan commented Nov 16, 2017

fixed

@fredericDelaporte fredericDelaporte changed the title Add test for GH1436 Add test for #1436 Nov 17, 2017
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 adjusted the tests, and I have added a potential fix. I think there is still some points to check, see comments details.

@@ -50,7 +51,7 @@ protected override void OnTearDown()
using (var s = OpenSession())
using (var tx = s.BeginTransaction())
{
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(1));
s.CreateQuery("delete from Book").ExecuteUpdate();
Copy link
Member

@fredericDelaporte fredericDelaporte Nov 17, 2017

Choose a reason for hiding this comment

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

This assert in teardown is indeed "completing" the CanLoadAndSaveObjectInDifferentSessions test. Removed and CanLoadAndSaveObjectInDifferentSessions test changed for correctly asserting itself all that it needs to assert.

using (ISession s = OpenSession())
{
var book = s.Get<Book>(2);
Assert.That(book, Is.Null);
Copy link
Member

Choose a reason for hiding this comment

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

The test fails when merging a not yet persisted entity. Since we use assigned ids, we must ensure that any future changes will not cause this test to start merging an already persisted entity.

@@ -230,6 +230,9 @@ private object MergeTransientEntity(object entity, string entityName, object req
else
{
copy = source.Instantiate(persister, id);
// #1436: we use the persister to instanciate a new, transient entity. In case it has lazy properties,
// we need to to tell its proxy that the properties are all fetched, since it is a transient entity.
persister.AfterInitialize(copy, false, source);
Copy link
Member

Choose a reason for hiding this comment

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

Minimal fix. I wonder whether it should be put here or inside SessionImpl.Instantiate called just above. The Instantiate method semantic does not seem to imply it may only be used on transient entities, that is why I have decided to put the fix here.

The stack overflow was occurring because the proxy FieldInterceptor was not initialized. In such case, trying to get it causes a sort of "endless self initialization" loop. Maybe I should try to fix that instead. I will give it a try later.

@fredericDelaporte fredericDelaporte self-assigned this Nov 17, 2017

if ("get_FieldInterceptor".Equals(methodName))
{
return null;
Copy link
Member

Choose a reason for hiding this comment

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

When the field interceptor is not set (we are here in the else part of an if (FieldInterceptor != null)) and is asked for, we should just yield null instead of trying to call it on the underlying Target (which was causing the stack overflow).

The "get_FieldInterceptor".Equals(methodName) test is debatable and causes issues if the entity itself has a FieldInterceptor property. But this case is already bugged due to other similar tests done in DefaultDynamicLazyFieldInterceptor, so adding it does not cause any regression. It will need a separated fix.

Copy link
Member

Choose a reason for hiding this comment

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

I think checks for set_FieldInterceptor/get_FieldInterceptor shall be at the beginning of the method, as in anyway the code inside is a duplicate.

@@ -230,9 +230,6 @@ private object MergeTransientEntity(object entity, string entityName, object req
else
{
copy = source.Instantiate(persister, id);
// #1436: we use the persister to instanciate a new, transient entity. In case it has lazy properties,
// we need to to tell its proxy that the properties are all fetched, since it is a transient entity.
persister.AfterInitialize(copy, false, source);
Copy link
Member

Choose a reason for hiding this comment

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

Fix no more needed with the alternate fix. Not having the proxy initialized is then equivalent to having it considering all properties are already fetched.

Ascertained by adding more asserts in the transient merge test.

@fredericDelaporte
Copy link
Member

@hazzik, if the refactoring is ok for you, I am going to rebase and squash all that to two commits, by author.

@@ -13,15 +13,25 @@ public class DefaultDynamicLazyFieldInterceptor : IFieldInterceptorAccessor, Pro
public object Intercept(InvocationInfo info)
{
var methodName = info.TargetMethod.Name;
if (FieldInterceptor != null)
var isPropertyGet = ReflectHelper.IsPropertyGet(info.TargetMethod);
var isPropertySet = !isPropertyGet && ReflectHelper.IsPropertySet(info.TargetMethod);
Copy link
Member

@hazzik hazzik Nov 18, 2017

Choose a reason for hiding this comment

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

!isPropertyGet && part isn't needed

@hazzik hazzik changed the title Add test for #1436 Fix StackOverflowException in DefaultDynamicLazyFieldInterceptor Nov 18, 2017
@hazzik
Copy link
Member

hazzik commented Nov 18, 2017

@fredericDelaporte I did a further refactoring and merged master into here. If you want to squash commits, I don't mind if my name would be lost in the process.

@fredericDelaporte
Copy link
Member

The point on names is mainly to keep commit associated with who has directly worked on the change. For the fix part, being you or me, no trouble, but I do not think it should be @offan since he may not have analyzed and followed the rational behind the fix. So if someone was later asking him explanations on the fix if he was set as the author, it would not be practical.

doroshenko and others added 2 commits November 19, 2017 00:30
…ties

 * Fixes #1436
 * Fixes a test which was relying on an assert in teardown.
 * Adjusts the new Merge test to ensure it tests its expected case.
@fredericDelaporte
Copy link
Member

Rebased and squashed.

@fredericDelaporte fredericDelaporte merged commit a218b4f into nhibernate:master Nov 19, 2017
@fredericDelaporte fredericDelaporte added this to the 5.1 milestone Nov 19, 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.

3 participants