diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java index 12446bb9d0ccc..b6ce7f8d0c35d 100644 --- a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java @@ -49,7 +49,12 @@ public void close() { * this is the original Panache-Query, if any (can be null) */ private String originalQuery; - protected String countQuery; + /** + * This is only used by the Spring Data JPA extension, due to Spring's Query annotation allowing a custom count query + * See https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html#jpa.query-methods.at-query.native + * Otherwise we do not use this, and rely on ORM to generate count queries + */ + protected String customCountQueryForSpring; private String orderBy; private Session session; @@ -73,11 +78,12 @@ public CommonPanacheQueryImpl(Session session, String query, String originalQuer this.paramsArrayOrMap = paramsArrayOrMap; } - private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String newQueryString, String countQuery, + private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String newQueryString, + String customCountQueryForSpring, Class projectionType) { this.session = previousQuery.session; this.query = newQueryString; - this.countQuery = countQuery; + this.customCountQueryForSpring = customCountQueryForSpring; this.orderBy = previousQuery.orderBy; this.paramsArrayOrMap = previousQuery.paramsArrayOrMap; this.page = previousQuery.page; @@ -106,16 +112,16 @@ public CommonPanacheQueryImpl project(Class type) { // If the query starts with a select clause, we pass it on to ORM which can handle that via a projection type if (lowerCasedTrimmedQuery.startsWith("select ")) { - // just pass it through - return new CommonPanacheQueryImpl<>(this, query, countQuery, type); + // I think projections do not change the result count, so we can keep the custom count query + return new CommonPanacheQueryImpl<>(this, query, customCountQueryForSpring, type); } // FIXME: this assumes the query starts with "FROM " probably? // build select clause with a constructor expression String selectClause = "SELECT " + getParametersFromClass(type, null); - return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, - "select count(*) " + selectQuery, null); + // I think projections do not change the result count, so we can keep the custom count query + return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, customCountQueryForSpring, null); } private StringBuilder getParametersFromClass(Class type, String parentParameter) { @@ -267,35 +273,27 @@ public void withHint(String hintName, Object value) { // Results - @SuppressWarnings("unchecked") public long count() { if (count == null) { - String selectQuery = query; - if (PanacheJpaUtil.isNamedQuery(query)) { - SelectionQuery q = session.createNamedSelectionQuery(query.substring(1)); - selectQuery = getQueryString(q); - } - - SelectionQuery countQuery = session.createSelectionQuery(countQuery(selectQuery)); - if (paramsArrayOrMap instanceof Map) - AbstractJpaOperations.bindParameters(countQuery, (Map) paramsArrayOrMap); - else - AbstractJpaOperations.bindParameters(countQuery, (Object[]) paramsArrayOrMap); - try (NonThrowingCloseable c = applyFilters()) { - count = (Long) countQuery.getSingleResult(); + if (customCountQueryForSpring != null) { + SelectionQuery countQuery = session.createSelectionQuery(customCountQueryForSpring, Long.class); + if (paramsArrayOrMap instanceof Map) + AbstractJpaOperations.bindParameters(countQuery, (Map) paramsArrayOrMap); + else + AbstractJpaOperations.bindParameters(countQuery, (Object[]) paramsArrayOrMap); + try (NonThrowingCloseable c = applyFilters()) { + count = countQuery.getSingleResult(); + } + } else { + SelectionQuery query = createBaseQuery(); + try (NonThrowingCloseable c = applyFilters()) { + count = query.getResultCount(); + } } } return count; } - private String countQuery(String selectQuery) { - if (countQuery != null) { - return countQuery; - } - - return PanacheJpaUtil.getFastCountQuery(selectQuery); - } - @SuppressWarnings("unchecked") public List list() { SelectionQuery hibernateQuery = createQuery(); diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/CustomCountPanacheQuery.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/CustomCountPanacheQuery.java index eae85ea681eb7..a5c59fe58ca7c 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/CustomCountPanacheQuery.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/CustomCountPanacheQuery.java @@ -14,7 +14,7 @@ public CustomCountPanacheQuery(Session session, SelectionQuery hibernateQuery, S super(new CommonPanacheQueryImpl<>(session, CommonPanacheQueryImpl.getQueryString(hibernateQuery), null, null, paramsArrayOrMap) { { - this.countQuery = customCountQuery; + this.customCountQueryForSpring = customCountQuery; } }); } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java index d4b07443db267..1c1e779a9cd71 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java @@ -38,7 +38,12 @@ public class CommonPanacheQueryImpl { * this is the original Panache-Query, if any (can be null) */ private String originalQuery; - protected String countQuery; + /** + * This is only used by the Spring Data JPA extension, due to Spring's Query annotation allowing a custom count query + * See https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html#jpa.query-methods.at-query.native + * Otherwise we do not use this, and rely on ORM to generate count queries + */ + protected String customCountQueryForSpring; private String orderBy; private Uni em; @@ -62,11 +67,12 @@ public CommonPanacheQueryImpl(Uni em, String query, String origi this.paramsArrayOrMap = paramsArrayOrMap; } - private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String newQueryString, String countQuery, + private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String newQueryString, + String customCountQueryForSpring, Class projectionType) { this.em = previousQuery.em; this.query = newQueryString; - this.countQuery = countQuery; + this.customCountQueryForSpring = customCountQueryForSpring; this.orderBy = previousQuery.orderBy; this.paramsArrayOrMap = previousQuery.paramsArrayOrMap; this.page = previousQuery.page; @@ -94,16 +100,16 @@ public CommonPanacheQueryImpl project(Class type) { // If the query starts with a select clause, we pass it on to ORM which can handle that via a projection type if (lowerCasedTrimmedQuery.startsWith("select ")) { - // just pass it through - return new CommonPanacheQueryImpl<>(this, query, countQuery, type); + // I think projections do not change the result count, so we can keep the custom count query + return new CommonPanacheQueryImpl<>(this, query, customCountQueryForSpring, type); } // FIXME: this assumes the query starts with "FROM " probably? // build select clause with a constructor expression String selectClause = "SELECT " + getParametersFromClass(type, null); - return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, - "select count(*) " + selectQuery, type); + // I think projections do not change the result count, so we can keep the custom count query + return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, customCountQueryForSpring, null); } private StringBuilder getParametersFromClass(Class type, String parentParameter) { @@ -263,34 +269,26 @@ public void withHint(String hintName, Object value) { @SuppressWarnings("unchecked") public Uni count() { - String selectQuery; - if (PanacheJpaUtil.isNamedQuery(query)) { - selectQuery = NamedQueryUtil.getNamedQuery(query.substring(1)); - } else { - selectQuery = query; - } - if (count == null) { // FIXME: question about caching the result here count = em.flatMap(session -> { - Mutiny.SelectionQuery countQuery = session.createSelectionQuery(countQuery(selectQuery), Long.class); - if (paramsArrayOrMap instanceof Map) - AbstractJpaOperations.bindParameters(countQuery, (Map) paramsArrayOrMap); - else - AbstractJpaOperations.bindParameters(countQuery, (Object[]) paramsArrayOrMap); - return applyFilters(session, () -> countQuery.getSingleResult()); + if (customCountQueryForSpring != null) { + Mutiny.SelectionQuery countQuery = session.createSelectionQuery(customCountQueryForSpring, + Long.class); + if (paramsArrayOrMap instanceof Map) + AbstractJpaOperations.bindParameters(countQuery, (Map) paramsArrayOrMap); + else + AbstractJpaOperations.bindParameters(countQuery, (Object[]) paramsArrayOrMap); + return applyFilters(session, () -> countQuery.getSingleResult()); + } else { + Mutiny.SelectionQuery query = createBaseQuery(session); + return applyFilters(session, () -> query.getResultCount()); + } }); } return count; } - private String countQuery(String selectQuery) { - if (countQuery != null) { - return countQuery; - } - return PanacheJpaUtil.getFastCountQuery(selectQuery); - } - @SuppressWarnings({ "unchecked", "rawtypes" }) public Uni> list() { return em.flatMap(session -> { diff --git a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java index 00f1cbf9e943e..75024176d7151 100644 --- a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java +++ b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java @@ -13,7 +13,7 @@ public CustomCountPanacheQuery(Uni em, String query, String cust Object paramsArrayOrMap) { super(new CommonPanacheQueryImpl(em, query, null, null, paramsArrayOrMap) { { - this.countQuery = customCountQuery; + this.customCountQueryForSpring = customCountQuery; } }); } diff --git a/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java b/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java index 6cfa4b97582c2..54b2b5936f51f 100644 --- a/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java +++ b/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java @@ -1,15 +1,8 @@ package io.quarkus.panache.hibernate.common.runtime; import java.util.Locale; -import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CommonTokenStream; -import org.hibernate.grammars.hql.HqlLexer; -import org.hibernate.grammars.hql.HqlParser; -import org.hibernate.grammars.hql.HqlParser.SelectStatementContext; - import io.quarkus.panache.common.Sort; import io.quarkus.panache.common.exception.PanacheQueryException; @@ -36,80 +29,6 @@ public class PanacheJpaUtil { static final Pattern WITH_PATTERN = Pattern.compile("^\\s*WITH\\s+.*", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); - /** - * This turns an HQL (already expanded from Panache-QL) query into a count query, using text manipulation - * if we can, because it's faster, or fall back to using the ORM HQL parser in {@link #getCountQueryUsingParser(String)} - */ - public static String getFastCountQuery(String query) { - // try to generate a good count query from the existing query - String countQuery; - // there are no fast ways to get rid of fetches, or WITH - if (FETCH_PATTERN.matcher(query).matches() - || WITH_PATTERN.matcher(query).matches()) { - return getCountQueryUsingParser(query); - } - // if it starts with select, we can optimise - Matcher selectMatcher = SELECT_PATTERN.matcher(query); - if (selectMatcher.matches()) { - // this one cannot be null - String firstSelection = selectMatcher.group(1).trim().toLowerCase(Locale.ROOT); - if (firstSelection.startsWith("distinct")) { - // if firstSelection matched distinct only, we have something wrong in our selection list, probably functions/parens - // so bail out - if (firstSelection.length() == 8) { - return getCountQueryUsingParser(query); - } - // this one can be null - String secondSelection = selectMatcher.group(2); - // we can only count distinct single columns - if (secondSelection != null && !secondSelection.trim().isEmpty()) { - throw new PanacheQueryException("Count query not supported for select query: " + query); - } - countQuery = "SELECT COUNT(" + firstSelection + ") " + selectMatcher.group(3); - } else { - // it's not distinct, forget the column list - countQuery = "SELECT COUNT(*) " + selectMatcher.group(3); - } - } else if (LONE_SELECT_PATTERN.matcher(query).matches()) { - // a select anywhere else in there might be tricky - return getCountQueryUsingParser(query); - } else if (FROM_PATTERN.matcher(query).matches()) { - countQuery = "SELECT COUNT(*) " + query; - } else { - throw new PanacheQueryException("Count query not supported for select query: " + query); - } - - // remove the order by clause - String lcQuery = countQuery.toLowerCase(); - int orderByIndex = lcQuery.lastIndexOf(" order by "); - if (orderByIndex != -1) { - countQuery = countQuery.substring(0, orderByIndex); - } - return countQuery; - } - - /** - * This turns an HQL (already expanded from Panache-QL) query into a count query, using the - * ORM HQL parser. Slow version, see {@link #getFastCountQuery(String)} for the fast version. - */ - public static String getCountQueryUsingParser(String query) { - HqlLexer lexer = new HqlLexer(CharStreams.fromString(query)); - CommonTokenStream tokens = new CommonTokenStream(lexer); - HqlParser parser = new HqlParser(tokens); - SelectStatementContext statement = parser.selectStatement(); - try { - CountParserVisitor visitor = new CountParserVisitor(); - statement.accept(visitor); - return visitor.result(); - } catch (RequiresSubqueryException x) { - // no luck - SubQueryAliasParserVisitor visitor = new SubQueryAliasParserVisitor(); - statement.accept(visitor); - String ret = visitor.result(); - return "select count( * ) from ( " + ret + " )"; - } - } - public static String getEntityName(Class entityClass) { // FIXME: not true? // Escape the entity name just in case some keywords are used diff --git a/extensions/panache/panache-hibernate-common/runtime/src/test/java/io/quarkus/panache/hibernate/common/runtime/CountTest.java b/extensions/panache/panache-hibernate-common/runtime/src/test/java/io/quarkus/panache/hibernate/common/runtime/CountTest.java deleted file mode 100644 index a9727e2b4cd6b..0000000000000 --- a/extensions/panache/panache-hibernate-common/runtime/src/test/java/io/quarkus/panache/hibernate/common/runtime/CountTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.quarkus.panache.hibernate.common.runtime; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class CountTest { - @Test - public void testParser() { - // one column, order/limit/offset - assertCountQueryUsingParser("select count( * ) from bar", "select foo from bar order by foo, bar ASC limit 2 offset 3"); - // two columns - assertCountQueryUsingParser("select count( * ) from bar", "select foo,gee from bar"); - // one column distinct - assertCountQueryUsingParser("select count( distinct foo ) from bar", "select distinct foo from bar"); - // two columns distinct - assertCountQueryUsingParser("select count( * ) from ( select distinct foo as __v0, gee as __v1 from bar )", - "select distinct foo,gee from bar"); - assertCountQueryUsingParser("select count( * ) from ( select distinct foo as __v0, gee as g from bar )", - "select distinct foo,gee as g from bar"); - // nested order by not touched - assertCountQueryUsingParser("select count( * ) from ( from entity order by id )", - "select foo from (from entity order by id) order by foo, bar ASC"); - // what happens to literals? - assertCountQueryUsingParser("select count( * ) from bar where some = 2 and other = '23'", - "select foo from bar where some = 2 and other = '23'"); - // fetches are gone - assertCountQueryUsingParser("select count( * ) from bar b", "select foo from bar b left join fetch b.things"); - // non-fetches remain - assertCountQueryUsingParser("select count( * ) from bar b left join b.things", - "select foo from bar b left join b.things"); - - // inverted select - assertCountQueryUsingParser("from bar select count( * )", "from bar select foo"); - // from without select - assertCountQueryUsingParser("from bar select count( * )", "from bar"); - - // CTE - assertFastCountQuery("WITH id AS ( SELECT p.id AS pid FROM Person2 AS p ) SELECT count( * ) FROM Person2 p", - "WITH id AS (SELECT p.id AS pid FROM Person2 AS p) SELECT p FROM Person2 p"); - } - - @Test - public void testFastVersion() { - // one column, order/limit/offset - assertFastCountQuery("SELECT COUNT(*) from bar", "select foo from bar order by foo, bar ASC limit 2 offset 3"); - // two columns - assertFastCountQuery("SELECT COUNT(*) from bar", "select foo,gee from bar"); - // one column distinct - assertFastCountQuery("SELECT COUNT(distinct foo) from bar", "select distinct foo from bar"); - // two columns distinct - Assertions.assertThrows(RuntimeException.class, () -> assertFastCountQuery("XX", "select distinct foo,gee from bar")); - // nested order by not touched - assertFastCountQuery("SELECT COUNT(*) from (from entity order by id)", - "select foo from (from entity order by id) order by foo, bar ASC"); - // what happens to literals? - assertFastCountQuery("SELECT COUNT(*) from bar where some = 2 and other = '23'", - "select foo from bar where some = 2 and other = '23'"); - // fetches are gone - assertFastCountQuery("select count( * ) from bar b", "select foo from bar b left join fetch b.things"); - // non-fetches remain - assertFastCountQuery("SELECT COUNT(*) from bar b left join b.things", "select foo from bar b left join b.things"); - - // inverted select - assertFastCountQuery("from bar select count( * )", "from bar select foo"); - // from without select - assertFastCountQuery("SELECT COUNT(*) from bar", "from bar"); - - // CTE - assertFastCountQuery("WITH id AS ( SELECT p.id AS pid FROM Person2 AS p ) SELECT count( * ) FROM Person2 p", - "WITH id AS (SELECT p.id AS pid FROM Person2 AS p) SELECT p FROM Person2 p"); - } - - private void assertCountQueryUsingParser(String expected, String selectQuery) { - String countQuery = PanacheJpaUtil.getCountQueryUsingParser(selectQuery); - Assertions.assertEquals(expected, countQuery); - } - - private void assertFastCountQuery(String expected, String selectQuery) { - String countQuery = PanacheJpaUtil.getFastCountQuery(selectQuery); - Assertions.assertEquals(expected, countQuery); - } -} diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/Bug40962Entity.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/Bug40962Entity.java new file mode 100644 index 0000000000000..2dcb607f8e71b --- /dev/null +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/Bug40962Entity.java @@ -0,0 +1,11 @@ +package io.quarkus.it.panache.defaultpu; + +import jakarta.persistence.Entity; + +import io.quarkus.hibernate.orm.panache.PanacheEntity; + +@Entity +public class Bug40962Entity extends PanacheEntity { + public String name; + public String location; +} diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/TestEndpoint.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/TestEndpoint.java index c6eed8c982a8a..440aa962ac3f0 100644 --- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/TestEndpoint.java +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/TestEndpoint.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1897,4 +1898,16 @@ public void runSomeTests42416() { // all good let's continue } } + + @GET + @Path("40962") + @Transactional + public String testBug40962() { + // should not throw + Bug40962Entity.find("name = :name ORDER BY locate(location, :location) DESC", + Map.of("name", "Demo", "location", "something")).count(); + Bug40962Entity.find("FROM Bug40962Entity WHERE name = :name ORDER BY locate(location, :location) DESC", + Map.of("name", "Demo", "location", "something")).count(); + return "OK"; + } } diff --git a/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/defaultpu/PanacheFunctionalityTest.java b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/defaultpu/PanacheFunctionalityTest.java index 823f0f5c1db06..b5322476c4359 100644 --- a/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/defaultpu/PanacheFunctionalityTest.java +++ b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/defaultpu/PanacheFunctionalityTest.java @@ -263,4 +263,9 @@ public void testBug31117() { public void testBug42416() { RestAssured.when().get("/test/42416").then().body(is("OK")); } + + @Test + public void testBug40962() { + RestAssured.when().get("/test/40962").then().body(is("OK")); + } } diff --git a/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/Bug40962Entity.java b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/Bug40962Entity.java new file mode 100644 index 0000000000000..629aae4115a62 --- /dev/null +++ b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/Bug40962Entity.java @@ -0,0 +1,11 @@ +package io.quarkus.it.panache.reactive; + +import jakarta.persistence.Entity; + +import io.quarkus.hibernate.reactive.panache.PanacheEntity; + +@Entity +public class Bug40962Entity extends PanacheEntity { + public String name; + public String location; +} diff --git a/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java index 9b655b59b1c67..42fc452618fda 100644 --- a/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java +++ b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java @@ -8,6 +8,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.function.Supplier; import java.util.stream.Stream; @@ -2113,4 +2114,18 @@ public Uni testBug36496() { return "OK"; }); } + + @GET + @Path("40962") + @WithTransaction + public Uni testBug40962() { + // should not throw + return Bug40962Entity.find("name = :name ORDER BY locate(location, :location) DESC", + Map.of("name", "Demo", "location", "something")).count() + .flatMap(count -> Bug40962Entity + .find("FROM Bug40962Entity WHERE name = :name ORDER BY locate(location, :location) DESC", + Map.of("name", "Demo", "location", "something")) + .count()) + .map(count -> "OK"); + } } diff --git a/integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/PanacheFunctionalityTest.java b/integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/PanacheFunctionalityTest.java index c7670ddbad1aa..b6c517f53a06f 100644 --- a/integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/PanacheFunctionalityTest.java +++ b/integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/PanacheFunctionalityTest.java @@ -314,4 +314,9 @@ public void testBug26308() { public void testBug36496() { RestAssured.when().get("/test/36496").then().body(is("OK")); } + + @Test + public void testBug40962() { + RestAssured.when().get("/test/40962").then().body(is("OK")); + } }