Skip to content
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

Exception executing HQL query with uncorrelated left joins in subselect #3334

Closed
csharper2010 opened this issue Jun 21, 2023 · 7 comments · Fixed by #3369
Closed

Exception executing HQL query with uncorrelated left joins in subselect #3334

csharper2010 opened this issue Jun 21, 2023 · 7 comments · Fixed by #3369

Comments

@csharper2010
Copy link
Contributor

The base situation

We have a data model like this one

Entity ---0..* ChildEntity
       ---0..1 OtherEntity

ChildEntity ---1 GrandChildEntity

The associations to OtherEntity and GrandChildEntity are mapped as many-to-one.

When executing the HQL query ...

SELECT ROOT 
FROM Entity AS ROOT 
WHERE
EXISTS 
    (FROM ELEMENTS(ROOT.Children) AS child
        LEFT JOIN child.Child AS grandChild
        LEFT JOIN ROOT.OtherEntity AS otherEntity
    WHERE
        grandChild.Name like 'G%'
        OR otherEntity.Name like 'G%'
    )

The problem

... we get the following sql generated:

select entity0_.Id as id1_0_3_, entity0_.Name as name2_0_3_, entity0_.OtherEntity as otherentity3_0_3_
from Entity entity0_ 
where exists (
    select children1_.Id, grandchild2_.Id, otherentit3_.Id
    from ChildEntity children1_
        left outer join GrandChildEntity grandchild2_ on children1_.Child=grandchild2_.Id, 
        left outer join OtherEntity otherentit3_ on entity0_.OtherEntity=otherentit3_.Id 
    where entity0_.Id=children1_.Parent and (grandchild2_.Name like 'G%' or otherentit3_.Name like 'G%'))

This leads to an exception because the comma , before the last left outer join is illegal.

Side note on why we don't use direct dot-navigations instead of explicit joins

Using implied joins on the :1-navigations like in the following query does not work because then, the joins are created as inner join or theta join so the condition is not evaluated correctly if there is not OtherEntity associated with Entity:

SELECT ROOT 
FROM Entity AS ROOT 
WHERE
    EXISTS 
        (FROM ELEMENTS(ROOT.Children) AS child
        WHERE
        child.Child.Name like 'G%'
        OR ROOT.OtherEntity.Name like 'A%'
)

I might start a separate discussion on Nullability and many-to-one because of that.

csharper2010 added a commit to csharper2010/nhibernate-core that referenced this issue Jun 21, 2023
Generate correct outer joins in case it is explicit in a subselect
@bahusoid
Copy link
Member

bahusoid commented Jun 21, 2023

 from ChildEntity children1_
--...
 LEFT JOIN ROOT.OtherEntity AS otherEntity

I would say it's invalid hql. What does this join do in subquery? It makes no sense to me.
Why is it not part of main query?

SELECT ROOT 
FROM Entity AS ROOT 
 LEFT JOIN ROOT.OtherEntity AS otherEntity -- MOVED HERE
WHERE
EXISTS 
    (FROM ELEMENTS(ROOT.Children) AS child
        LEFT JOIN child.Child AS grandChild       
    WHERE
        grandChild.Name like 'G%'
        OR otherEntity.Name like 'G%'
    )

@csharper2010
Copy link
Contributor Author

I would say it's invalid hql. What does this join do in subquery? It makes no sense to me.
Why is it not part of main query?

HQL documentation does not mention any restrictions on such joins and the same way you can place outer joins in SQL wherever you want, it could be handled in HQL.

That query worked fine in NHibernate 1.2.1. and it is not hand-written HQL but generated from our application.

@bahusoid
Copy link
Member

It's another case of #1228

It's actually mentioned in original issue discussion that provided hql looks wrong. I also already mentioned it: #2146 (comment)

@csharper2010
Copy link
Contributor Author

There is nothing mentioned in HQL doc that it is illegal and it worked (quite far) in the past.

I guess you would allow a query where an implied join is made like so:

SELECT ROOT 
FROM Entity AS ROOT 
WHERE
    EXISTS 
        (FROM ELEMENTS(ROOT.Children) AS child
        WHERE
        child.Child.Name like 'G%'
        OR ROOT.OtherEntity.Name like 'A%'
)

Then why an explicit left join should be forbidden? In some cases (depending on the mapping), this would even lead to an outer join in the generated SQL inside the subselect. But not in mine... I get a theta join with that hql so I would like to have the explicit outer join.

@bahusoid
Copy link
Member

Ok. Ideally, we should support such nonsensical joins too. Only your initial fix is wrong. In #2146 implicit join is generated instead of requested explicit join. I had to add some hacks too properly handle it in #2990 (see 32fdfec)

Your current PR is just another hack supporting your specific case. If you need such joins fix it properly and remove my hack.

The following query would still fail with your suggested fix:

q = session.CreateQuery(
	@"
	SELECT ROOT 
	FROM Entity AS ROOT 
	WHERE
		EXISTS 
			(FROM ChildEntity AS child
				LEFT JOIN child.Child AS grandChild
				LEFT JOIN ROOT.OtherEntity AS otherEntity
			WHERE
				grandChild.Name like 'A%'
				OR otherEntity.Name like 'A%'
			)");
Assert.AreEqual(1, q.List().Count);

@csharper2010
Copy link
Contributor Author

Yep, I also have a feeling that my fix won't handle all the posssible cases...
The tree generation is very complex and I don't have the knowledge about corner cases that may exist so it's hard to estimate all the consequences of a change.

It would be ideal if the code in #2146 would not be required, it was a workaround already then but it was driven by the comment that the from clause processor will take care of adding it to the right place.

I would be willing to do some further steps but it's really not an easy task because of those complex interdependencies.

csharper2010 added a commit to csharper2010/nhibernate-core that referenced this issue Jun 26, 2023
Convert EntityJoin to FROM_FRAGMENT only for first element
Adjust comment in HqlSqlWalker before adding an element without Parent to the tree and use AppendFromElement instead of direct AddChild
@csharper2010 csharper2010 mentioned this issue Jun 26, 2023
@csharper2010
Copy link
Contributor Author

Now I had some time to work on a cleaner fix: #3338

@bahusoid: the query you gave in your comment now also works because I do the translation based on the explicit case where necessary.

bahusoid added a commit that referenced this issue Jul 21, 2023
…3369)

Fixes #3334

Co-authored-by: CSharper2010 <csharper2010@googlemail.com>
Co-authored-by: Frédéric Delaporte <12201973+fredericDelaporte@users.noreply.github.com>
@fredericDelaporte fredericDelaporte added this to the 5.5 milestone Jul 21, 2023
wvopt pushed a commit to csharper2010/nhibernate-core that referenced this issue Dec 10, 2024
Generate correct outer joins in case it is explicit in a subselect
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants