From a8f979f4f69c69816d531b239f5a3a0a136826f4 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 15 Jul 2024 15:23:53 +0200 Subject: [PATCH] wip --- .../blazebit/persistence/spi/DbmsDialect.java | 8 ++++++++ .../persistence/impl/JoinManager.java | 7 +++++-- .../impl/dialect/DefaultDbmsDialect.java | 5 +++++ .../impl/dialect/H2DbmsDialect.java | 7 +++++++ .../testsuite/DelegatingDbmsDialect.java | 5 +++++ .../persistence/testsuite/SubqueryTest.java | 18 ++++++++++++++++-- .../persistence/testsuite/TreatTest.java | 19 ++++++++++++------- .../src/test/resources/logging.properties | 2 +- .../testsuite/limit/LimitOneToManyTest.java | 2 +- 9 files changed, 60 insertions(+), 13 deletions(-) diff --git a/core/api/src/main/java/com/blazebit/persistence/spi/DbmsDialect.java b/core/api/src/main/java/com/blazebit/persistence/spi/DbmsDialect.java index b991889fe5..fac442f00c 100644 --- a/core/api/src/main/java/com/blazebit/persistence/spi/DbmsDialect.java +++ b/core/api/src/main/java/com/blazebit/persistence/spi/DbmsDialect.java @@ -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. diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/JoinManager.java b/core/impl/src/main/java/com/blazebit/persistence/impl/JoinManager.java index c400fb7e0a..b526af16bb 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/JoinManager.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/JoinManager.java @@ -1018,9 +1018,9 @@ private JoinResult correlate(JoinResult result, String rootAlias, Expression cor alias = aliasManager.generateRootAlias(alias); } JoinAliasInfo rootAliasInfo = new JoinAliasInfo(alias, alias, true, true, aliasManager); - JoinNode joinNode = JoinNode.createEntityJoinNode(result.baseNode, JoinType.LEFT, result.baseNode.getEntityType(), rootAliasInfo, false); - result.baseNode.addEntityJoin(joinNode); + 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); @@ -1963,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 diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/DefaultDbmsDialect.java b/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/DefaultDbmsDialect.java index 1b3b7f2d06..fdd59d263b 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/DefaultDbmsDialect.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/DefaultDbmsDialect.java @@ -284,6 +284,11 @@ public boolean supportsNestedCorrelations() { return true; } + @Override + public boolean supportsCorrelationInJoinOnClause() { + return true; + } + protected String getWindowFunctionDummyOrderBy() { return null; } diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/H2DbmsDialect.java b/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/H2DbmsDialect.java index 59f4611ad1..b962a58794 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/H2DbmsDialect.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/dialect/H2DbmsDialect.java @@ -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; + } } diff --git a/core/testsuite/src/main/java/com/blazebit/persistence/testsuite/DelegatingDbmsDialect.java b/core/testsuite/src/main/java/com/blazebit/persistence/testsuite/DelegatingDbmsDialect.java index fc9fe48659..4ad4d77fc0 100644 --- a/core/testsuite/src/main/java/com/blazebit/persistence/testsuite/DelegatingDbmsDialect.java +++ b/core/testsuite/src/main/java/com/blazebit/persistence/testsuite/DelegatingDbmsDialect.java @@ -112,6 +112,11 @@ public boolean supportsNestedCorrelations() { return delegate.supportsNestedCorrelations(); } + @Override + public boolean supportsCorrelationInJoinOnClause() { + return delegate.supportsCorrelationInJoinOnClause(); + } + @Override public boolean supportsWithClauseInModificationQuery() { return delegate.supportsWithClauseInModificationQuery(); diff --git a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/SubqueryTest.java b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/SubqueryTest.java index 60198a8ee5..06d67859cb 100644 --- a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/SubqueryTest.java +++ b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/SubqueryTest.java @@ -351,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(); } @@ -365,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(); } diff --git a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/TreatTest.java b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/TreatTest.java index 7a2a245e10..414d101912 100644 --- a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/TreatTest.java +++ b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/TreatTest.java @@ -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(); } } diff --git a/core/testsuite/src/test/resources/logging.properties b/core/testsuite/src/test/resources/logging.properties index f307bc07b8..7c6d881baa 100644 --- a/core/testsuite/src/test/resources/logging.properties +++ b/core/testsuite/src/test/resources/logging.properties @@ -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 diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/limit/LimitOneToManyTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/limit/LimitOneToManyTest.java index 9b335a2e6b..c02d748cf2 100644 --- a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/limit/LimitOneToManyTest.java +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/limit/LimitOneToManyTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2023 Blazebit. + * Copyright 2014 - 2024 Blazebit. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.