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

[#1811] Don't generate root for intermediate from node of correlated join path #1812

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ public interface DbmsDialect {
* @since 1.5.0
*/
public boolean supportsNestedCorrelations();

/**
* Returns true if the dbms supports correlations in the JOIN ON clause, false otherwise.
*
* @return Whether correlations are supported by the dbms in the JOIN ON clause
* @since 1.6.12
*/
public boolean supportsCorrelationInJoinOnClause();

/**
* Returns true if the dbms supports the with clause in modification queries, false otherwise.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1017,15 +1017,20 @@ private JoinResult correlate(JoinResult result, String rootAlias, Expression cor
if (!aliasManager.isAliasAvailable(alias)) {
alias = aliasManager.generateRootAlias(alias);
}
String baseAlias = addRoot(result.baseNode.getEntityType(), alias, false);
JoinNode joinNode = ((JoinAliasInfo) aliasManager.getAliasInfo(baseAlias)).getJoinNode();
joinNode.getAliasInfo().setImplicit(true);
Predicate correlationPredicate = expressionFactory.createBooleanExpression(createCorrelationPredicate(result.baseNode.getEntityType(), result.baseNode.getAliasExpression(), baseAlias), false);
JoinAliasInfo rootAliasInfo = new JoinAliasInfo(alias, alias, true, true, aliasManager);
JoinNode joinNode = JoinNode.createEntityJoinNode(result.baseNode, JoinType.INNER, result.baseNode.getEntityType(), rootAliasInfo, false);
rootAliasInfo.setJoinNode(joinNode);
rootNodes.add(joinNode);
explicitJoinNodes.add(joinNode);
// register root alias in aliasManager
aliasManager.registerAliasInfo(rootAliasInfo);

Predicate correlationPredicate = expressionFactory.createBooleanExpression(createCorrelationPredicate(result.baseNode.getEntityType(), result.baseNode.getAliasExpression(), alias), false);
correlationPredicate.accept(joinVisitor);
joinNode.setOnPredicate(new CompoundPredicate(CompoundPredicate.BooleanOperator.AND, correlationPredicate));
if (implicit || !(correlatedAttributeExpr instanceof ArrayExpression)) {
PathExpression pathExpression = new PathExpression();
pathExpression.getExpressions().add(new PropertyExpression(baseAlias));
pathExpression.getExpressions().add(new PropertyExpression(alias));
if (correlatedAttributeExpr instanceof PathExpression) {
pathExpression.getExpressions().addAll(((PathExpression) correlatedAttributeExpr).getExpressions());
} else {
Expand Down Expand Up @@ -1958,6 +1963,9 @@ private boolean shouldEmulateEntityJoin(JoinNode node) {
if (node.getJoinType() != JoinType.INNER) {
return false;
}
if (!mainQuery.dbmsDialect.supportsCorrelationInJoinOnClause() && node.isEntityJoinNode() && node.getAliasInfo().getAliasOwner() != node.getParent().getAliasInfo().getAliasOwner()) {
return true;
}
// in Hibernate < 5.1, we weren't able to refer to non-driving table aliases in the ON clause which can be worked around by emulating through a cross join
if (!mainQuery.jpaProvider.supportsNonDrivingAliasInOnClause()) {
// But this only works when the parent join node has no RIGHT or FULL joins
Expand Down Expand Up @@ -2031,6 +2039,14 @@ private boolean renderCorrelationJoinPath(StringBuilder sb, JoinNode joinBase, J
}
whereConjuncts.add(whereSb.toString());
return true;
} else if (!externalRepresentation && !node.isLateral()) {
sb.append(joinBase.getEntityType().getName());
sb.append(" _synthetic_");
sb.append(node.getAlias());
sb.append(" JOIN _synthetic_");
sb.append(node.getAlias());
sb.append('.').append(correlationPath);
return true;
}
} else {
boolean renderAlias = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ public boolean supportsNestedCorrelations() {
return true;
}

@Override
public boolean supportsCorrelationInJoinOnClause() {
return true;
}

protected String getWindowFunctionDummyOrderBy() {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,11 @@ public Character getDefaultEscapeCharacter() {
public boolean supportsArbitraryLengthMultiset() {
return true;
}

@Override
public boolean supportsCorrelationInJoinOnClause() {
// It's not possible to do `from Document d where exists (select 1 from Person p join Document d2 on d = d2)`
// i.e. refer to the join alias of the outer query from the ON condition within a subquery
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public boolean supportsNestedCorrelations() {
return delegate.supportsNestedCorrelations();
}

@Override
public boolean supportsCorrelationInJoinOnClause() {
return delegate.supportsCorrelationInJoinOnClause();
}

@Override
public boolean supportsWithClauseInModificationQuery() {
return delegate.supportsWithClauseInModificationQuery();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,13 @@ public void testSubqueryImplicitCorrelate() {
.where("d.owner.friend.name").isNotNull()
.where("d.owner.defaultLanguage").isNotNull()
.end();
String expectedQuery = "SELECT d FROM Document d WHERE EXISTS (SELECT 1 FROM Person p, Document d_owner_base JOIN d_owner_base.owner owner_1 LEFT JOIN owner_1.friend friend_1 WHERE d.id = d_owner_base.id AND friend_1.name IS NOT NULL AND owner_1.defaultLanguage IS NOT NULL)";
String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "SELECT 1 FROM Person p JOIN Document d_owner_base ON (d.id = d_owner_base.id) JOIN d_owner_base.owner owner_1 LEFT JOIN owner_1.friend friend_1 WHERE friend_1.name IS NOT NULL AND owner_1.defaultLanguage IS NOT NULL";
} else {
expectedSubQuery = "SELECT 1 FROM Person p, Document d_owner_base JOIN d_owner_base.owner owner_1 LEFT JOIN owner_1.friend friend_1 WHERE d.id = d_owner_base.id AND friend_1.name IS NOT NULL AND owner_1.defaultLanguage IS NOT NULL";
}
String expectedQuery = "SELECT d FROM Document d WHERE EXISTS (" + expectedSubQuery + ")";
assertEquals(expectedQuery, crit.getQueryString());
crit.getResultList();
}
Expand Down Expand Up @@ -345,7 +351,14 @@ public void testSubqueryCorrelatesArrayExpression() {
.from("Document[_ MEMBER OF d.owner.ownedDocuments AND LENGTH(d.owner.name) > 0]", "dSub")
.where("dSub").notEqExpression("d")
.end();
String expectedQuery = "SELECT d FROM Document d WHERE EXISTS (SELECT 1 FROM Document dSub, Document d_owner_base JOIN d_owner_base.owner owner_1 WHERE dSub MEMBER OF owner_1.ownedDocuments AND LENGTH(owner_1.name) > 0 AND d.id = d_owner_base.id AND dSub <> d)";

String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "SELECT 1 FROM Document dSub JOIN Document d_owner_base" + onClause("d.id = d_owner_base.id") + " JOIN d_owner_base.owner owner_1 WHERE dSub MEMBER OF owner_1.ownedDocuments AND LENGTH(owner_1.name) > 0 AND dSub <> d";
} else {
expectedSubQuery = "SELECT 1 FROM Document dSub, Document d_owner_base JOIN d_owner_base.owner owner_1 WHERE dSub MEMBER OF owner_1.ownedDocuments AND LENGTH(owner_1.name) > 0 AND d.id = d_owner_base.id AND dSub <> d";
}
String expectedQuery = "SELECT d FROM Document d WHERE EXISTS (" + expectedSubQuery + ")";
assertEquals(expectedQuery, crit.getQueryString());
crit.getResultList();
}
Expand All @@ -359,7 +372,14 @@ public void testSubqueryCorrelatesArrayExpressionEntityEqual() {
.from("Document[_ MEMBER OF d.owner.ownedDocuments AND LENGTH(d.owner.name) > 0]", "dSub")
.where("dSub").notEqExpression("d")
.end();
String expectedQuery = "SELECT d FROM Document d WHERE EXISTS (SELECT 1 FROM Document dSub, Document d_owner_base JOIN d_owner_base.owner owner_1 WHERE dSub MEMBER OF owner_1.ownedDocuments AND LENGTH(owner_1.name) > 0 AND d = d_owner_base AND dSub <> d)";

String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "SELECT 1 FROM Document dSub JOIN Document d_owner_base" + onClause("d = d_owner_base") + " JOIN d_owner_base.owner owner_1 WHERE dSub MEMBER OF owner_1.ownedDocuments AND LENGTH(owner_1.name) > 0 AND dSub <> d";
} else {
expectedSubQuery = "SELECT 1 FROM Document dSub, Document d_owner_base JOIN d_owner_base.owner owner_1 WHERE dSub MEMBER OF owner_1.ownedDocuments AND LENGTH(owner_1.name) > 0 AND d = d_owner_base AND dSub <> d";
}
String expectedQuery = "SELECT d FROM Document d WHERE EXISTS (" + expectedSubQuery + ")";
assertEquals(expectedQuery, crit.getQueryString());
crit.getResultList();
}
Expand Down Expand Up @@ -494,11 +514,20 @@ public void testSubqueryCollectionAccessAddsJoin() {
.where("LENGTH(d.partners.localized[1])").gt(1)
.end()
.like().value("%dld").noEscape();
String expectedQuery = "SELECT d FROM Document d"
+ " WHERE (SELECT p.name FROM Person p, Document d_partners_base " +
String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "SELECT p.name FROM Person p JOIN Document d_partners_base ON (d.id = d_partners_base.id) " +
"LEFT JOIN d_partners_base.partners partners_1 " +
"LEFT JOIN partners_1.localized localized_1_1" + onClause("KEY(localized_1_1) = 1") +
" WHERE LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0";
} else {
expectedSubQuery = "SELECT p.name FROM Person p, Document d_partners_base " +
"LEFT JOIN d_partners_base.partners partners_1 " +
"LEFT JOIN partners_1.localized localized_1_1" + onClause("KEY(localized_1_1) = 1") +
" WHERE d.id = d_partners_base.id AND LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0) LIKE :param_1";
" WHERE d.id = d_partners_base.id AND LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0";
}
String expectedQuery = "SELECT d FROM Document d"
+ " WHERE (" + expectedSubQuery + ") LIKE :param_1";
assertEquals(expectedQuery, crit.getQueryString());
crit.getResultList();
}
Expand Down Expand Up @@ -536,7 +565,12 @@ public void testSubqueryAddsJoin() {
.groupBy("id")
.orderByAsc("localizedCount");

String expectedSubQuery = "ABS((SELECT COUNT(" + joinAliasValue("localized_1") + ") FROM Person p LEFT JOIN p.localized localized_1, Document d_contacts_base LEFT JOIN d_contacts_base.contacts contacts_1 WHERE d.id = d_contacts_base.id AND p.id = " + joinAliasValue("contacts_1", "id") + "))";
String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "ABS((SELECT COUNT(" + joinAliasValue("localized_1") + ") FROM Person p LEFT JOIN p.localized localized_1 JOIN Document d_contacts_base ON (d.id = d_contacts_base.id) LEFT JOIN d_contacts_base.contacts contacts_1 WHERE p.id = " + joinAliasValue("contacts_1", "id") + "))";
} else {
expectedSubQuery = "ABS((SELECT COUNT(" + joinAliasValue("localized_1") + ") FROM Person p LEFT JOIN p.localized localized_1, Document d_contacts_base LEFT JOIN d_contacts_base.contacts contacts_1 WHERE d.id = d_contacts_base.id AND p.id = " + joinAliasValue("contacts_1", "id") + "))";
}
String expectedQuery = "SELECT d.id, " + expectedSubQuery + " AS localizedCount "
+ "FROM Document d GROUP BY d.id ORDER BY localizedCount ASC";
assertEquals(expectedQuery, cb.getQueryString());
Expand All @@ -553,11 +587,20 @@ public void testSubqueryCollectionAccess() {
.end()
.like().value("%dld").noEscape();

String expectedQuery = "SELECT d FROM Document d"
+ " WHERE (SELECT p.name FROM Person p, Document d_partners_base " +
String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "SELECT p.name FROM Person p JOIN Document d_partners_base ON (d.id = d_partners_base.id) " +
"LEFT JOIN d_partners_base.partners partners_1 " +
"LEFT JOIN partners_1.localized localized_1_1" + onClause("KEY(localized_1_1) = 1") +
" WHERE d.id = d_partners_base.id AND LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0) LIKE :param_1";
" WHERE LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0";
} else {
expectedSubQuery = "SELECT p.name FROM Person p, Document d_partners_base " +
"LEFT JOIN d_partners_base.partners partners_1 " +
"LEFT JOIN partners_1.localized localized_1_1" + onClause("KEY(localized_1_1) = 1") +
" WHERE d.id = d_partners_base.id AND LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0";
}
String expectedQuery = "SELECT d FROM Document d"
+ " WHERE (" + expectedSubQuery + ") LIKE :param_1";
assertEquals(expectedQuery, crit.getQueryString());
crit.getResultList();
}
Expand All @@ -573,12 +616,21 @@ public void testMultipleJoinPathSubqueryCollectionAccess() {
.end()
.like().value("%dld").noEscape();

String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "SELECT p.name FROM Person p JOIN Person d_partners_localized_base ON (partners_1.id = d_partners_localized_base.id) " +
"LEFT JOIN d_partners_localized_base.localized localized_1_1"
+ onClause("KEY(localized_1_1) = 1")
+ " WHERE LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0";
} else {
expectedSubQuery = "SELECT p.name FROM Person p, Person d_partners_localized_base LEFT JOIN d_partners_localized_base.localized localized_1_1"
+ onClause("KEY(localized_1_1) = 1")
+ " WHERE partners_1.id = d_partners_localized_base.id AND LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0";
}
String expectedQuery = "SELECT d FROM Document d "
+ "LEFT JOIN d.partners partners_1 "
+ "LEFT JOIN partners_1.localized l "
+ "WHERE (SELECT p.name FROM Person p, Person d_partners_localized_base LEFT JOIN d_partners_localized_base.localized localized_1_1"
+ onClause("KEY(localized_1_1) = 1")
+ " WHERE partners_1.id = d_partners_localized_base.id AND LENGTH("+ joinAliasValue("localized_1_1") + ") > :param_0) LIKE :param_1";
+ "WHERE (" + expectedSubQuery + ") LIKE :param_1";
assertEquals(expectedQuery, crit.getQueryString());
crit.getResultList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,17 +252,22 @@ public void implicitJoinTreatedImplicitCorrelation() {
.select("1")
.where("TREAT(p.children.container.child AS PolymorphicSub1).id").eqExpression("sub1.id")
.end();
assertEquals(
"SELECT p FROM PolymorphicBase p " +
"WHERE EXISTS (" +
"SELECT 1 FROM PolymorphicSub1 sub1, PolymorphicBase p_children_base " +
String expectedSubQuery;
if (jpaProvider.supportsEntityJoin()) {
expectedSubQuery = "SELECT 1 FROM PolymorphicSub1 sub1 JOIN PolymorphicBase p_children_base" + onClause("p.id = p_children_base.id") + " " +
"LEFT JOIN p_children_base.children children_1 " +
"LEFT JOIN children_1.container container_1 " +
"LEFT JOIN container_1.child child_1 " +
"WHERE " + treatRoot("child_1", PolymorphicSub1.class, "id") + " = sub1.id";
} else {
expectedSubQuery = "SELECT 1 FROM PolymorphicSub1 sub1, PolymorphicBase p_children_base " +
"LEFT JOIN p_children_base.children children_1 " +
"LEFT JOIN children_1.container container_1 " +
"LEFT JOIN container_1.child child_1 " +
"WHERE p.id = p_children_base.id " +
"AND " + treatRoot("child_1", PolymorphicSub1.class, "id") + " = sub1.id)",
crit.getQueryString()
);
"AND " + treatRoot("child_1", PolymorphicSub1.class, "id") + " = sub1.id";
}
assertEquals("SELECT p FROM PolymorphicBase p WHERE EXISTS (" + expectedSubQuery + ")", crit.getQueryString());
crit.getResultList();
}
}
2 changes: 1 addition & 1 deletion core/testsuite/src/test/resources/logging.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ handlers = java.util.logging.ConsoleHandler
org.hibernate.level = SEVERE
org.hibernate.tool.hbm2ddl.level = OFF
org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl.level = ALL
#org.hibernate.SQL.level = ALL
org.hibernate.SQL.level = ALL
#org.hibernate.type.descriptor.sql.level = ALL
#org.hibernate.tool.hbm2ddl.level = ALL
#org.hibernate.pretty.level = ALL
Expand Down
Loading
Loading